From 0f8efcc358d21d90154f05ed7db9675f30bb0420 Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Mon, 29 Apr 2024 06:42:33 +0000 Subject: [PATCH] feat : fix archunit violations --- .../mfscreener/helper/NavSearchHelper.java | 40 -------- .../mfscreener/helper/SpringContext.java | 22 ----- .../models/portfolio/MergedTransaction.java | 27 +++++ .../models/portfolio/Transaction.java | 6 ++ .../repository/MFSchemeRepository.java | 1 - .../service/CapitalGainsService.java | 19 ++-- .../FIFOUnitsService.java} | 99 +++++++------------ .../GainEntryService.java} | 60 ++++++----- .../mfscreener/service/NavService.java | 12 +++ .../mfscreener/service/SchemeService.java | 2 + .../mfscreener/utils/FundTypeUtility.java | 33 +++++++ 11 files changed, 161 insertions(+), 160 deletions(-) delete mode 100644 src/main/java/com/learning/mfscreener/helper/NavSearchHelper.java delete mode 100644 src/main/java/com/learning/mfscreener/helper/SpringContext.java create mode 100644 src/main/java/com/learning/mfscreener/models/portfolio/MergedTransaction.java create mode 100644 src/main/java/com/learning/mfscreener/models/portfolio/Transaction.java rename src/main/java/com/learning/mfscreener/{utils/FIFOUnits.java => service/FIFOUnitsService.java} (79%) rename src/main/java/com/learning/mfscreener/{utils/GainEntry.java => service/GainEntryService.java} (82%) create mode 100644 src/main/java/com/learning/mfscreener/utils/FundTypeUtility.java diff --git a/src/main/java/com/learning/mfscreener/helper/NavSearchHelper.java b/src/main/java/com/learning/mfscreener/helper/NavSearchHelper.java deleted file mode 100644 index 29de848..0000000 --- a/src/main/java/com/learning/mfscreener/helper/NavSearchHelper.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.learning.mfscreener.helper; - -import com.learning.mfscreener.entities.MFSchemeEntity; -import com.learning.mfscreener.repository.MFSchemeRepository; -import com.learning.mfscreener.service.HistoricalNavService; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.Optional; -import org.springframework.util.StringUtils; - -public class NavSearchHelper { - - private static final LocalDate GRAND_FATHERED_DATE = LocalDate.of(2018, 1, 31); - - public static BigDecimal getNav(String isin) { - MFSchemeRepository mfSchemeRepository = SpringContext.getBean(MFSchemeRepository.class); - Long schemeCode = mfSchemeRepository.getSchemeIdByISIN(isin).orElseGet(() -> { - HistoricalNavService historicalNavService = SpringContext.getBean(HistoricalNavService.class); - String historicalGrandFatheredValue = - historicalNavService.getHistoricalGrandFatheredValue(isin, GRAND_FATHERED_DATE); - return StringUtils.hasText(historicalGrandFatheredValue) - ? Long.valueOf(historicalGrandFatheredValue) - : null; - }); - if (schemeCode != null) { - Optional mfSchemeDTO = - mfSchemeRepository.findBySchemeIdAndMfSchemeNavEntities_NavDate(schemeCode, GRAND_FATHERED_DATE); - if (mfSchemeDTO.isPresent()) { - Float nav = mfSchemeDTO.get().getMfSchemeNavEntities().get(0).getNav(); - return BigDecimal.valueOf(nav); - } - } - - return BigDecimal.ZERO; - } - - private NavSearchHelper() { - throw new UnsupportedOperationException("Constructor can't be initialized"); - } -} diff --git a/src/main/java/com/learning/mfscreener/helper/SpringContext.java b/src/main/java/com/learning/mfscreener/helper/SpringContext.java deleted file mode 100644 index b1a30da..0000000 --- a/src/main/java/com/learning/mfscreener/helper/SpringContext.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.learning.mfscreener.helper; - -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Component; - -@Component -public class SpringContext implements ApplicationContextAware { - - private static ApplicationContext context; - - public static T getBean(Class beanClass) { - return context.getBean(beanClass); - } - - @Override - public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException { - context = applicationContext; - } -} diff --git a/src/main/java/com/learning/mfscreener/models/portfolio/MergedTransaction.java b/src/main/java/com/learning/mfscreener/models/portfolio/MergedTransaction.java new file mode 100644 index 0000000..4027617 --- /dev/null +++ b/src/main/java/com/learning/mfscreener/models/portfolio/MergedTransaction.java @@ -0,0 +1,27 @@ +package com.learning.mfscreener.models.portfolio; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +public class MergedTransaction { + private final LocalDate date; + private final List transactions; + + public MergedTransaction(LocalDate date) { + this.date = date; + this.transactions = new ArrayList<>(); + } + + public void add(UserTransactionDTO txn) { + this.transactions.add(txn); + } + + public LocalDate getDate() { + return date; + } + + public List getTransactions() { + return transactions; + } +} diff --git a/src/main/java/com/learning/mfscreener/models/portfolio/Transaction.java b/src/main/java/com/learning/mfscreener/models/portfolio/Transaction.java new file mode 100644 index 0000000..55e1926 --- /dev/null +++ b/src/main/java/com/learning/mfscreener/models/portfolio/Transaction.java @@ -0,0 +1,6 @@ +package com.learning.mfscreener.models.portfolio; + +import java.math.BigDecimal; +import java.time.LocalDate; + +public record Transaction(LocalDate txnDate, BigDecimal units, BigDecimal nav, BigDecimal tax) {} diff --git a/src/main/java/com/learning/mfscreener/repository/MFSchemeRepository.java b/src/main/java/com/learning/mfscreener/repository/MFSchemeRepository.java index 2697fd6..9f962cc 100644 --- a/src/main/java/com/learning/mfscreener/repository/MFSchemeRepository.java +++ b/src/main/java/com/learning/mfscreener/repository/MFSchemeRepository.java @@ -45,7 +45,6 @@ where upper(m.fundHouse) like upper(:fName) order by m.schemeId @Transactional(readOnly = true) List findByFundHouseLikeIgnoringCaseOrderBySchemeIdAsc(@Param("fName") String fName); - @Cacheable("schemeIdByISIN") @Query("select m.schemeId from MFSchemeEntity m where m.payOut = :isin") Optional getSchemeIdByISIN(@Param("isin") String isin); diff --git a/src/main/java/com/learning/mfscreener/service/CapitalGainsService.java b/src/main/java/com/learning/mfscreener/service/CapitalGainsService.java index 07c4183..32d7d57 100644 --- a/src/main/java/com/learning/mfscreener/service/CapitalGainsService.java +++ b/src/main/java/com/learning/mfscreener/service/CapitalGainsService.java @@ -10,8 +10,6 @@ import com.learning.mfscreener.models.portfolio.UserSchemeDTO; import com.learning.mfscreener.models.portfolio.UserTransactionDTO; import com.learning.mfscreener.models.response.ProcessCasResponse; -import com.learning.mfscreener.utils.FIFOUnits; -import com.learning.mfscreener.utils.GainEntry; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Comparator; @@ -32,9 +30,15 @@ public class CapitalGainsService { private BigDecimal investedAmount = BigDecimal.ZERO; private Double currentValue = 0D; - private final List gainEntries = new ArrayList<>(); + private final List gainEntries = new ArrayList<>(); private final List errors = new ArrayList<>(); + private final FIFOUnitsService fifoUnitsService; + + public CapitalGainsService(FIFOUnitsService fifoUnitsService) { + this.fifoUnitsService = fifoUnitsService; + } + @Loggable(params = false) ProcessCasResponse processData(CasDTO casDTO) throws IncompleteCASError { casDTO.folios().forEach(this::processFolio); @@ -64,7 +68,7 @@ void validateOpenBalance(UserSchemeDTO scheme) { void processTransactions(Fund fund, List transactions, UserSchemeDTO scheme) { try { - FIFOUnits fifo = new FIFOUnits(fund, transactions); + FIFOUnitsService fifo = fifoUnitsService.init(fund, transactions); investedAmount = this.investedAmount.add(fifo.getTotalInvested()); currentValue += scheme.valuation().value(); gainEntries.addAll(fifo.getRecordedGains()); @@ -73,11 +77,12 @@ void processTransactions(Fund fund, List transactions, UserS } } - Map> prepareGains(List gainEntries) { + Map> prepareGains(List gainEntries) { - gainEntries.sort(Comparator.comparing(GainEntry::getFinYear).thenComparing(GainEntry::getFundType)); + gainEntries.sort( + Comparator.comparing(GainEntryService::getFinYear).thenComparing(GainEntryService::getFundType)); - Map> groupedGains = + Map> groupedGains = gainEntries.stream().collect(Collectors.groupingBy(txn -> txn.getFinYear() + "#" + txn.getFundType())); Map> summary = new HashMap<>(); diff --git a/src/main/java/com/learning/mfscreener/utils/FIFOUnits.java b/src/main/java/com/learning/mfscreener/service/FIFOUnitsService.java similarity index 79% rename from src/main/java/com/learning/mfscreener/utils/FIFOUnits.java rename to src/main/java/com/learning/mfscreener/service/FIFOUnitsService.java index a142030..997f9f5 100644 --- a/src/main/java/com/learning/mfscreener/utils/FIFOUnits.java +++ b/src/main/java/com/learning/mfscreener/service/FIFOUnitsService.java @@ -1,10 +1,15 @@ -package com.learning.mfscreener.utils; +package com.learning.mfscreener.service; import com.learning.mfscreener.exception.GainsException; import com.learning.mfscreener.models.portfolio.Fund; import com.learning.mfscreener.models.portfolio.FundType; +import com.learning.mfscreener.models.portfolio.MergedTransaction; +import com.learning.mfscreener.models.portfolio.Transaction; import com.learning.mfscreener.models.portfolio.TransactionType; import com.learning.mfscreener.models.portfolio.UserTransactionDTO; +import com.learning.mfscreener.utils.AppConstants; +import com.learning.mfscreener.utils.FundTypeUtility; +import com.learning.mfscreener.utils.LocalDateUtility; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; @@ -19,31 +24,38 @@ import java.util.stream.IntStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; -public class FIFOUnits { +@Service +public class FIFOUnitsService { - private static final Logger LOGGER = LoggerFactory.getLogger(FIFOUnits.class); + private static final Logger LOGGER = LoggerFactory.getLogger(FIFOUnitsService.class); - private final Fund fund; - private final FundType fundType; + private Fund fund; + private FundType fundType; private final Deque transactionsQueue = new ArrayDeque<>(); + private final GainEntryService gainEntryService; private BigDecimal totalInvested = BigDecimal.ZERO; private BigDecimal currentBalance = BigDecimal.ZERO; - private List recordedGains = new ArrayList<>(); + private List recordedGains = new ArrayList<>(); - public FIFOUnits(Fund fund, List userTransactionDTOList) throws GainsException { + public FIFOUnitsService(GainEntryService gainEntryService) { + this.gainEntryService = gainEntryService; + } + + public FIFOUnitsService init(Fund fund, List userTransactionDTOList) { this.fund = fund; this.fundType = determineFundType(fund.type(), userTransactionDTOList); Map mergedTransactions = mergeTransactions(userTransactionDTOList); - processTransactions(mergedTransactions); + return this; } FundType determineFundType(String type, List transactions) { FundType fundType; if (!"EQUITY".equals(type) && !"DEBT".equals(type)) { - fundType = deriveFundTypeFromTransactions(transactions); + fundType = FundTypeUtility.deriveFundTypeFromTransactions(transactions); } else { fundType = FundType.valueOf(type); } @@ -77,8 +89,8 @@ void processTransactions(Map mergedTransactions) { transactionDates.forEach(localDate -> processTransactionDate(localDate, mergedTransactions.get(localDate))); } - private void processTransactionDate(LocalDate dt, MergedTransaction mergedTransaction) { - List userTransactionDTOS = mergedTransaction.transactions; + void processTransactionDate(LocalDate dt, MergedTransaction mergedTransaction) { + List userTransactionDTOS = mergedTransaction.getTransactions(); if (userTransactionDTOS.size() == 2 && dt.isAfter(AppConstants.TAX_STARTED_DATE)) { findTaxFromTransactionsAndProcess(userTransactionDTOS, dt); } else if (userTransactionDTOS.size() > 2 && dt.isAfter(AppConstants.TAX_STARTED_DATE)) { @@ -88,7 +100,7 @@ private void processTransactionDate(LocalDate dt, MergedTransaction mergedTransa } } - private void processMultipleTransactions(List userTransactionDTOS, LocalDate dt) { + void processMultipleTransactions(List userTransactionDTOS, LocalDate dt) { int splitIndex = 0; boolean found = false; for (int i = 0; i < userTransactionDTOS.size(); i++) { @@ -112,7 +124,7 @@ private void processMultipleTransactions(List userTransactio processesGroupedTransactions(dt, sellTransactionList, true); } - private void processesGroupedTransactions( + void processesGroupedTransactions( LocalDate dt, List userTransactionDTOList, boolean sellOrder) { if (!userTransactionDTOList.isEmpty()) { groupTransactions(userTransactionDTOList, sellOrder) @@ -120,8 +132,7 @@ private void processesGroupedTransactions( } } - private List> groupTransactions( - List transactionDTOList, boolean sellOrder) { + List> groupTransactions(List transactionDTOList, boolean sellOrder) { // If list size is less than or equal to 2, no need to regroup if (transactionDTOList.size() <= 2) { return Collections.singletonList(transactionDTOList); @@ -145,7 +156,7 @@ private List> groupTransactions( .toList(); } - private void processStandardTransactions(List userTransactionDTOS) { + void processStandardTransactions(List userTransactionDTOS) { userTransactionDTOS.forEach(userTransactionDTO -> { if (userTransactionDTO.units() == null) { LOGGER.error("Unhandled dividend Transactions"); @@ -155,7 +166,7 @@ private void processStandardTransactions(List userTransactio }); } - private void processTransaction(UserTransactionDTO userTransactionDTO) { + void processTransaction(UserTransactionDTO userTransactionDTO) { if (userTransactionDTO.units() > 0) { buy( userTransactionDTO.date(), @@ -171,7 +182,7 @@ private void processTransaction(UserTransactionDTO userTransactionDTO) { } } - private void findTaxFromTransactionsAndProcess(List userTransactionDTOS, LocalDate dt) { + void findTaxFromTransactionsAndProcess(List userTransactionDTOS, LocalDate dt) { // if buy we will have STAMP_DUTY_TAX, for sale we will have STT_TAX if (userTransactionDTOS.size() == 2) { if (userTransactionDTOS.get(1).type().compareTo(TransactionType.STAMP_DUTY_TAX) == 0) { @@ -210,13 +221,13 @@ private void findTaxFromTransactionsAndProcess(List userTran } } - private void buy(LocalDate txnDate, BigDecimal quantity, BigDecimal nav, BigDecimal tax) { + void buy(LocalDate txnDate, BigDecimal quantity, BigDecimal nav, BigDecimal tax) { transactionsQueue.add(new Transaction(txnDate, quantity, nav, tax)); totalInvested = totalInvested.add(quantity.multiply(nav)); currentBalance = currentBalance.add(quantity); } - private void sell(LocalDate sellDate, BigDecimal quantity, BigDecimal nav, BigDecimal tax) throws GainsException { + void sell(LocalDate sellDate, BigDecimal quantity, BigDecimal nav, BigDecimal tax) throws GainsException { String finYear = LocalDateUtility.getFinYear(sellDate); BigDecimal originalQuantity = quantity.abs(); BigDecimal pendingUnits = originalQuantity; @@ -239,7 +250,7 @@ private void sell(LocalDate sellDate, BigDecimal quantity, BigDecimal nav, BigDe BigDecimal stampDuty = purchaseTax.multiply(gainUnits).divide(units, 2, RoundingMode.HALF_UP); BigDecimal stt = tax.multiply(gainUnits).divide(originalQuantity, 2, RoundingMode.HALF_UP); - GainEntry ge = new GainEntry( + GainEntryService ge = gainEntryService.init( finYear, fund, fundType, @@ -267,61 +278,21 @@ private void sell(LocalDate sellDate, BigDecimal quantity, BigDecimal nav, BigDe } } - /** - * - * Detect Fund Type. - * - UNKNOWN if there are no redemption transactions - * - EQUITY if STT_TAX transactions are present in the portfolio - * - DEBT if no STT_TAX transactions are present along with redemptions - * - * @param transactions list of transactions for a single fund parsed from the CAS - * @return type of fund - */ - private FundType deriveFundTypeFromTransactions(List transactions) { - boolean valid = transactions.stream() - .anyMatch(x -> x.units() != null && x.units() < 0 && x.type() != TransactionType.REVERSAL); - - if (!valid) { - return FundType.UNKNOWN; - } - if (transactions.stream().anyMatch(x -> x.type() == TransactionType.STT_TAX)) { - return FundType.EQUITY; - } else { - return FundType.DEBT; - } - } - public BigDecimal getTotalInvested() { return totalInvested; } - public FIFOUnits setTotalInvested(BigDecimal totalInvested) { + public FIFOUnitsService setTotalInvested(BigDecimal totalInvested) { this.totalInvested = totalInvested; return this; } - public List getRecordedGains() { + public List getRecordedGains() { return recordedGains; } - public FIFOUnits setRecordedGains(List recordedGains) { + public FIFOUnitsService setRecordedGains(List recordedGains) { this.recordedGains = recordedGains; return this; } - - private static class MergedTransaction { - private final LocalDate date; - private final List transactions; - - public MergedTransaction(LocalDate date) { - this.date = date; - this.transactions = new ArrayList<>(); - } - - public void add(UserTransactionDTO txn) { - this.transactions.add(txn); - } - } - - private record Transaction(LocalDate txnDate, BigDecimal units, BigDecimal nav, BigDecimal tax) {} } diff --git a/src/main/java/com/learning/mfscreener/utils/GainEntry.java b/src/main/java/com/learning/mfscreener/service/GainEntryService.java similarity index 82% rename from src/main/java/com/learning/mfscreener/utils/GainEntry.java rename to src/main/java/com/learning/mfscreener/service/GainEntryService.java index b196f13..d6b7160 100644 --- a/src/main/java/com/learning/mfscreener/utils/GainEntry.java +++ b/src/main/java/com/learning/mfscreener/service/GainEntryService.java @@ -1,21 +1,24 @@ -package com.learning.mfscreener.utils; +package com.learning.mfscreener.service; -import com.learning.mfscreener.helper.NavSearchHelper; import com.learning.mfscreener.models.portfolio.Fund; import com.learning.mfscreener.models.portfolio.FundType; import com.learning.mfscreener.models.portfolio.GainType; +import com.learning.mfscreener.utils.LocalDateUtility; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.util.HashMap; import java.util.Map; +import org.springframework.stereotype.Service; -public class GainEntry { +@Service +public class GainEntryService { private final Map CII = loadCostInflationIndexData(); - private final LocalDate cutoffDate; - private final LocalDate sellCutoffDate; + private final NavService navService; + private LocalDate cutoffDate; + private LocalDate sellCutoffDate; private String finYear; private Fund fund; private FundType fundType; @@ -31,7 +34,7 @@ public class GainEntry { private String cachedIsin; private BigDecimal cachedNav; - public GainEntry( + public GainEntryService init( String finYear, Fund fund, FundType fundType, @@ -59,18 +62,23 @@ public GainEntry( this.cutoffDate = LocalDate.of(2018, 1, 31); this.sellCutoffDate = LocalDate.of(2018, 4, 1); updateNav(fund.isin()); + return this; + } + + public GainEntryService(NavService navService) { + this.navService = navService; } - private void updateNav(String isin) { + public void updateNav(String isin) { this.cachedIsin = isin; - this.cachedNav = NavSearchHelper.getNav(isin); + this.cachedNav = navService.getNavByISINOnDate(isin, LocalDate.of(2018, 1, 31)); } public String getFinYear() { return finYear; } - public GainEntry setFinYear(String finYear) { + public GainEntryService setFinYear(String finYear) { this.finYear = finYear; return this; } @@ -79,7 +87,7 @@ public Fund getFund() { return fund; } - public GainEntry setFund(Fund fund) { + public GainEntryService setFund(Fund fund) { this.fund = fund; return this; } @@ -88,7 +96,7 @@ public FundType getFundType() { return fundType; } - public GainEntry setFundType(FundType fundType) { + public GainEntryService setFundType(FundType fundType) { this.fundType = fundType; return this; } @@ -97,7 +105,7 @@ public LocalDate getPurchaseDate() { return purchaseDate; } - public GainEntry setPurchaseDate(LocalDate purchaseDate) { + public GainEntryService setPurchaseDate(LocalDate purchaseDate) { this.purchaseDate = purchaseDate; return this; } @@ -106,7 +114,7 @@ public BigDecimal getPurchaseNav() { return purchaseNav; } - public GainEntry setPurchaseNav(BigDecimal purchaseNav) { + public GainEntryService setPurchaseNav(BigDecimal purchaseNav) { this.purchaseNav = purchaseNav; return this; } @@ -115,7 +123,7 @@ public BigDecimal getPurchaseValue() { return purchaseValue; } - public GainEntry setPurchaseValue(BigDecimal purchaseValue) { + public GainEntryService setPurchaseValue(BigDecimal purchaseValue) { this.purchaseValue = purchaseValue; return this; } @@ -124,7 +132,7 @@ public BigDecimal getStampDuty() { return stampDuty; } - public GainEntry setStampDuty(BigDecimal stampDuty) { + public GainEntryService setStampDuty(BigDecimal stampDuty) { this.stampDuty = stampDuty; return this; } @@ -133,7 +141,7 @@ public LocalDate getSaleDate() { return saleDate; } - public GainEntry setSaleDate(LocalDate saleDate) { + public GainEntryService setSaleDate(LocalDate saleDate) { this.saleDate = saleDate; return this; } @@ -142,7 +150,7 @@ public BigDecimal getSaleNav() { return saleNav; } - public GainEntry setSaleNav(BigDecimal saleNav) { + public GainEntryService setSaleNav(BigDecimal saleNav) { this.saleNav = saleNav; return this; } @@ -151,7 +159,7 @@ public BigDecimal getSaleValue() { return saleValue; } - public GainEntry setSaleValue(BigDecimal saleValue) { + public GainEntryService setSaleValue(BigDecimal saleValue) { this.saleValue = saleValue; return this; } @@ -160,7 +168,7 @@ public BigDecimal getStt() { return stt; } - public GainEntry setStt(BigDecimal stt) { + public GainEntryService setStt(BigDecimal stt) { this.stt = stt; return this; } @@ -169,7 +177,7 @@ public BigDecimal getUnits() { return units; } - public GainEntry setUnits(BigDecimal units) { + public GainEntryService setUnits(BigDecimal units) { this.units = units; return this; } @@ -202,7 +210,7 @@ public BigDecimal getCoa() { return this.purchaseValue; } - private BigDecimal getFmv() { + public BigDecimal getFmv() { BigDecimal fmvNav = this.getFmvNav(); if (fmvNav == null) { return this.purchaseValue; @@ -210,14 +218,14 @@ private BigDecimal getFmv() { return fmvNav.multiply(this.getUnits()); } - private BigDecimal getFmvNav() { + public BigDecimal getFmvNav() { if (!this.fund.isin().equals(this.cachedIsin)) { this.updateNav(this.fund.isin()); } return this.cachedNav; } - private BigDecimal getIndexRatio() { + public BigDecimal getIndexRatio() { return CII.get(LocalDateUtility.getFinYear(this.saleDate)) .divide(CII.get(LocalDateUtility.getFinYear(this.purchaseDate)), 2, RoundingMode.HALF_UP); } @@ -236,11 +244,11 @@ public BigDecimal getStcg() { return BigDecimal.ZERO; } - private BigDecimal getGain() { + public BigDecimal getGain() { return this.saleValue.subtract(this.purchaseValue).setScale(2, RoundingMode.HALF_UP); } - private GainType getGainType() { + public GainType getGainType() { Map ltcg = new HashMap<>(); ltcg.put(FundType.EQUITY.name(), this.purchaseDate.plusYears(1)); ltcg.put(FundType.DEBT.name(), this.purchaseDate.plusYears(3)); @@ -248,7 +256,7 @@ private GainType getGainType() { return this.saleDate.isAfter(ltcg.get(this.fundType.name())) ? GainType.LTCG : GainType.STCG; } - private Map loadCostInflationIndexData() { + public Map loadCostInflationIndexData() { Map ciiDataMap = new HashMap<>(); ciiDataMap.put("FY2001-02", BigDecimal.valueOf(100)); ciiDataMap.put("FY2002-03", BigDecimal.valueOf(105)); diff --git a/src/main/java/com/learning/mfscreener/service/NavService.java b/src/main/java/com/learning/mfscreener/service/NavService.java index dacc046..ccb5c2a 100644 --- a/src/main/java/com/learning/mfscreener/service/NavService.java +++ b/src/main/java/com/learning/mfscreener/service/NavService.java @@ -5,7 +5,9 @@ import com.learning.mfscreener.models.MFSchemeDTO; import com.learning.mfscreener.utils.AppConstants; import com.learning.mfscreener.utils.LocalDateUtility; +import java.math.BigDecimal; import java.time.LocalDate; +import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -39,6 +41,16 @@ public MFSchemeDTO getNavOnDate(Long schemeCode, LocalDate inputDate) { return getNavByDateWithRetry(schemeCode, adjustedDate); } + @Loggable + public BigDecimal getNavByISINOnDate(String isin, LocalDate inputDate) { + Optional schemeIdByISIN = schemeService.getSchemeIdByISIN(isin); + if (schemeIdByISIN.isPresent()) { + MFSchemeDTO navByDateWithRetry = getNavByDateWithRetry(schemeIdByISIN.get(), inputDate); + return BigDecimal.valueOf(Long.parseLong(navByDateWithRetry.nav())); + } + return BigDecimal.ZERO; + } + @Loggable public MFSchemeDTO getNavByDateWithRetry(Long schemeCode, LocalDate navDate) { LOGGER.info("Fetching Nav for AMFISchemeCode: {} for date: {} from Cache", schemeCode, navDate); diff --git a/src/main/java/com/learning/mfscreener/service/SchemeService.java b/src/main/java/com/learning/mfscreener/service/SchemeService.java index 7e96c56..e3da277 100644 --- a/src/main/java/com/learning/mfscreener/service/SchemeService.java +++ b/src/main/java/com/learning/mfscreener/service/SchemeService.java @@ -19,6 +19,7 @@ import org.hibernate.exception.ConstraintViolationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.cache.annotation.Cacheable; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatusCode; import org.springframework.stereotype.Service; @@ -152,6 +153,7 @@ public Optional findByPayOut(String isin) { return mfSchemeRepository.findByPayOut(isin); } + @Cacheable("schemeIdByISIN") @Transactional(readOnly = true) public Optional getSchemeIdByISIN(String isin) { return mfSchemeRepository.getSchemeIdByISIN(isin); diff --git a/src/main/java/com/learning/mfscreener/utils/FundTypeUtility.java b/src/main/java/com/learning/mfscreener/utils/FundTypeUtility.java new file mode 100644 index 0000000..3c39841 --- /dev/null +++ b/src/main/java/com/learning/mfscreener/utils/FundTypeUtility.java @@ -0,0 +1,33 @@ +package com.learning.mfscreener.utils; + +import com.learning.mfscreener.models.portfolio.FundType; +import com.learning.mfscreener.models.portfolio.TransactionType; +import com.learning.mfscreener.models.portfolio.UserTransactionDTO; +import java.util.List; + +public class FundTypeUtility { + + /** + * + * Detect Fund Type. + * - UNKNOWN if there are no redemption transactions + * - EQUITY if STT_TAX transactions are present in the portfolio + * - DEBT if no STT_TAX transactions are present along with redemptions + * + * @param transactions list of transactions for a single fund parsed from the CAS + * @return type of fund + */ + public static FundType deriveFundTypeFromTransactions(List transactions) { + boolean valid = transactions.stream() + .anyMatch(x -> x.units() != null && x.units() < 0 && x.type() != TransactionType.REVERSAL); + + if (!valid) { + return FundType.UNKNOWN; + } + if (transactions.stream().anyMatch(x -> x.type() == TransactionType.STT_TAX)) { + return FundType.EQUITY; + } else { + return FundType.DEBT; + } + } +}