Skip to content

Commit

Permalink
[feat] �배당금 엔티티에 필드 추가 및 포트폴리오 필드 추가 (#451)
Browse files Browse the repository at this point in the history
* feat: Portfolio의 LocalDate 모킹

* feat: StockDividend 필드에 isDeleted 컬럼 추가
  • Loading branch information
yonghwankim-dev authored Aug 29, 2024
1 parent cfe38e0 commit 4ff3f23
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import codesquad.fineants.domain.dividend.domain.reader.HolidayFileReader;
import codesquad.fineants.domain.kis.repository.HolidayRepository;
import codesquad.fineants.domain.stock.domain.entity.Stock;
import codesquad.fineants.infra.s3.dto.Dividend;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
Expand Down Expand Up @@ -56,6 +55,8 @@ public class StockDividend extends BaseEntity {
private LocalDate exDividendDate;
@Column(name = "payment_date")
private LocalDate paymentDate;
@Column(name = "is_deleted", nullable = false)
private boolean isDeleted;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ticker_symbol")
private Stock stock;
Expand All @@ -70,6 +71,7 @@ private StockDividend(Long id, Money dividend, LocalDate recordDate, LocalDate e
this.recordDate = recordDate;
this.exDividendDate = exDividendDate;
this.paymentDate = paymentDate;
this.isDeleted = false;
this.stock = stock;
}

Expand Down Expand Up @@ -180,10 +182,6 @@ public boolean hasInRange(LocalDate from, LocalDate to) {
return recordDate.isAfter(from) && recordDate.isBefore(to);
}

public Dividend toDividend() {
return Dividend.create(recordDate, paymentDate, stock.getTickerSymbol(), stock.getCompanyName(), dividend);
}

public boolean equalPaymentDate(LocalDate paymentDate) {
if (this.paymentDate == null || paymentDate == null) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,24 @@

public interface StockDividendRepository extends JpaRepository<StockDividend, Long> {

@Query("select sd from StockDividend sd join fetch sd.stock s order by s.tickerSymbol, sd.recordDate")
@Query("select sd from StockDividend sd join fetch sd.stock s "
+ "where sd.isDeleted = false "
+ "order by s.tickerSymbol, sd.recordDate")
List<StockDividend> findAllStockDividends();

@Query("select sd from StockDividend sd join fetch sd.stock s where s.tickerSymbol = :tickerSymbol order by s.tickerSymbol, sd.recordDate")
@Query("select sd from StockDividend sd join fetch sd.stock s "
+ "where s.tickerSymbol = :tickerSymbol and sd.isDeleted = false "
+ "order by s.tickerSymbol, sd.recordDate")
List<StockDividend> findStockDividendsByTickerSymbol(@Param("tickerSymbol") String tickerSymbol);

@Query("select sd from StockDividend sd join fetch sd.stock s where s.tickerSymbol = :tickerSymbol and sd.recordDate = :recordDate order by s.tickerSymbol, sd.recordDate")
@Query("select sd from StockDividend sd join fetch sd.stock s "
+ "where s.tickerSymbol = :tickerSymbol and sd.recordDate = :recordDate and sd.isDeleted = false "
+ "order by s.tickerSymbol, sd.recordDate")
Optional<StockDividend> findByTickerSymbolAndRecordDate(@Param("tickerSymbol") String tickerSymbol,
@Param("recordDate") LocalDate recordDate);

@Modifying
@Query("delete from StockDividend sd where sd.stock.tickerSymbol in :tickerSymbols")
@Query("update StockDividend sd set sd.isDeleted = true "
+ "where sd.stock.tickerSymbol in :tickerSymbols")
int deleteByTickerSymbols(@Param("tickerSymbols") Set<String> tickerSymbols);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import codesquad.fineants.domain.notification.domain.entity.type.NotificationType;
import codesquad.fineants.domain.notification.repository.NotificationSentRepository;
import codesquad.fineants.domain.notificationpreference.domain.entity.NotificationPreference;
import codesquad.fineants.global.common.time.DefaultLocalDateTimeService;
import codesquad.fineants.global.common.time.LocalDateTimeService;
import codesquad.fineants.global.errors.errorcode.PortfolioErrorCode;
import codesquad.fineants.global.errors.exception.BadRequestException;
import codesquad.fineants.global.errors.exception.FineAntsException;
Expand All @@ -50,6 +52,7 @@
import jakarta.persistence.NamedSubgraph;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.persistence.UniqueConstraint;
import lombok.AccessLevel;
import lombok.Getter;
Expand Down Expand Up @@ -101,6 +104,9 @@ public class Portfolio extends BaseEntity implements Notifiable {
@OneToMany(mappedBy = "portfolio")
private final List<PortfolioHolding> portfolioHoldings = new ArrayList<>();

@Transient
private LocalDateTimeService localDateTimeService = new DefaultLocalDateTimeService();

private Portfolio(Long id, String name, String securitiesFirm, Money budget, Money targetGain, Money maximumLoss,
Boolean targetGainIsActive, Boolean maximumLossIsActive, Member member) {
validateBudget(budget, targetGain, maximumLoss);
Expand Down Expand Up @@ -252,7 +258,8 @@ public Expression calculateBalance() {
// 총 연간 배당금 = 각 종목들의 연배당금의 합계
public Expression calculateAnnualDividend() {
return portfolioHoldings.stream()
.map(portfolioHolding -> portfolioHolding.createMonthlyDividendMap(LocalDate.now()))
.map(portfolioHolding -> portfolioHolding.createMonthlyDividendMap(
localDateTimeService.getLocalDateWithNow()))
.map(map -> map.values().stream()
.reduce(Money.zero(), Expression::plus))
.reduce(Money.zero(), Expression::plus);
Expand Down Expand Up @@ -360,7 +367,7 @@ public boolean reachedMaximumLoss() {
public List<PortfolioPieChartItem> createPieChart() {
List<PortfolioPieChartItem> stocks = portfolioHoldings.stream()
.map(portfolioHolding -> portfolioHolding.createPieChartItem(calculateWeightBy(portfolioHolding)))
.collect(Collectors.toList());
.toList();
Bank bank = Bank.getInstance();
Percentage weight = calculateCashWeight().toPercentage(bank, Currency.KRW);
PortfolioPieChartItem cash = PortfolioPieChartItem.cash(weight, bank.toWon(calculateBalance()));
Expand Down Expand Up @@ -406,14 +413,14 @@ public List<PortfolioDividendChartItem> createDividendChart(LocalDate currentLoc
Currency to = Currency.KRW;
return totalDividendMap.entrySet().stream()
.map(entry -> PortfolioDividendChartItem.create(entry.getKey(), entry.getValue().reduce(bank, to)))
.collect(Collectors.toList());
.toList();
}

public List<PortfolioSectorChartItem> createSectorChart() {
return calculateSectorCurrentValuationMap().entrySet().stream()
.map(mappingSectorChartItem())
.sorted(PortfolioSectorChartItem::compareTo)
.collect(Collectors.toList());
.toList();
}

private Map<String, List<Expression>> calculateSectorCurrentValuationMap() {
Expand Down Expand Up @@ -533,4 +540,8 @@ public NotifyMessage getTargetPriceMessage(String token) {
public List<PortfolioHolding> getPortfolioHoldings() {
return Collections.unmodifiableList(portfolioHoldings);
}

public void setLocalDateTimeService(LocalDateTimeService localDateTimeService) {
this.localDateTimeService = localDateTimeService;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

public interface StockRepository extends JpaRepository<Stock, Long> {

@Query("select s from Stock s where s.isDeleted = false")
List<Stock> findAllStocks();

Optional<Stock> findByTickerSymbol(String tickerSymbol);

@Query("select s from Stock s where s.tickerSymbol in :tickerSymbols")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ private Mono<Map<Boolean, List<Stock>>> fetchPartitionedStocksForDelisted() {

@NotNull
private Set<String> findAllTickerSymbols() {
return stockRepository.findAll()
return stockRepository.findAllStocks()
.stream()
.map(Stock::getTickerSymbol)
.collect(Collectors.toUnmodifiableSet());
Expand Down
17 changes: 17 additions & 0 deletions src/test/java/codesquad/fineants/AbstractContainerBaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,23 @@ protected List<StockDividend> createStockDividendWith(Stock stock) {
);
}

protected List<StockDividend> createStockDividendThisYearWith(Stock stock) {
return List.of(
createStockDividend(
LocalDate.of(2024, 3, 31), LocalDate.of(2024, 3, 29),
LocalDate.of(2024, 5, 17),
stock),
createStockDividend(
LocalDate.of(2024, 6, 30), LocalDate.of(2024, 6, 28),
LocalDate.of(2024, 8, 16),
stock),
createStockDividend(
LocalDate.of(2024, 9, 30), LocalDate.of(2024, 9, 27),
LocalDate.of(2024, 11, 20),
stock)
);
}

protected Cookie[] createTokenCookies() {
TokenFactory tokenFactory = new TokenFactory();
Token token = Token.create("accessToken", "refreshToken");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.BDDMockito.*;

import java.time.LocalDate;
import java.time.LocalDateTime;
Expand All @@ -14,6 +15,7 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.security.core.context.SecurityContextHolder;

import codesquad.fineants.AbstractContainerBaseTest;
Expand Down Expand Up @@ -49,6 +51,7 @@
import codesquad.fineants.domain.purchasehistory.repository.PurchaseHistoryRepository;
import codesquad.fineants.domain.stock.domain.entity.Stock;
import codesquad.fineants.domain.stock.repository.StockRepository;
import codesquad.fineants.global.common.time.LocalDateTimeService;
import codesquad.fineants.global.errors.errorcode.MemberErrorCode;
import codesquad.fineants.global.errors.errorcode.PortfolioHoldingErrorCode;
import codesquad.fineants.global.errors.exception.FineAntsException;
Expand Down Expand Up @@ -86,6 +89,9 @@ class PortfolioHoldingServiceTest extends AbstractContainerBaseTest {
@Autowired
private ClosingPriceRepository closingPriceRepository;

@SpyBean
private LocalDateTimeService localDateTimeService;

@MockBean
private PortfolioHoldingEventPublisher publisher;

Expand All @@ -95,8 +101,10 @@ void readMyPortfolioStocks() {
// given
Member member = memberRepository.save(createMember());
Portfolio portfolio = portfolioRepository.save(createPortfolio(member));
portfolio.setLocalDateTimeService(localDateTimeService);
given(localDateTimeService.getLocalDateWithNow()).willReturn(LocalDate.of(2024, 1, 1));
Stock stock = stockRepository.save(createSamsungStock());
stockDividendRepository.saveAll(createStockDividendWith(stock));
stockDividendRepository.saveAll(createStockDividendThisYearWith(stock));
PortfolioHolding portfolioHolding = portFolioHoldingRepository.save(createPortfolioHolding(portfolio, stock));

LocalDateTime purchaseDate = LocalDateTime.of(2023, 9, 26, 9, 30, 0);
Expand Down Expand Up @@ -133,7 +141,7 @@ void readMyPortfolioStocks() {
Percentage dailyGainRate = RateDivision.of(dailyGain, totalInvestmentAmount)
.toPercentage(Bank.getInstance(), Currency.KRW);

Expression totalAnnualDividend = Money.won(361 * 3 * 4);
Money totalAnnualDividend = Money.won(361 * 3 * 3);
Expression currentValuation = Money.won(180000);
Percentage annualDividendYield = RateDivision.of(totalAnnualDividend, currentValuation)
.toPercentage(Bank.getInstance(), Currency.KRW);
Expand All @@ -152,7 +160,7 @@ void readMyPortfolioStocks() {
() -> assertThat(details.getDailyGain()).isEqualByComparingTo(Money.won(30000L)),
() -> assertThat(details.getDailyGainRate()).isEqualByComparingTo(dailyGainRate),
() -> assertThat(details.getBalance()).isEqualByComparingTo(Money.won(850000L)),
() -> assertThat(details.getAnnualDividend()).isEqualByComparingTo(Money.won(4332L)),
() -> assertThat(details.getAnnualDividend()).isEqualByComparingTo(totalAnnualDividend),
() -> assertThat(details.getAnnualDividendYield()).isEqualByComparingTo(annualDividendYield),
() -> assertThat(details.getProvisionalLossBalance()).isEqualByComparingTo(Money.won(0L)),
() -> assertThat(details.getTargetGainNotify()).isTrue(),
Expand Down Expand Up @@ -183,7 +191,7 @@ void readMyPortfolioStocks() {
Percentage.from(0.2),
Money.won(30000),
Percentage.from(0.2),
Money.won(4332)
Money.won(3249)
)
),
() -> assertThat(response.getPortfolioHoldings())
Expand Down

0 comments on commit 4ff3f23

Please sign in to comment.