EvaluationStateMapperService.java

package com.tdmconsult.ete.evaluation;

import static com.tdmconsult.ete.accounting.BalanceRecords.ZERO_AMOUNT;

import com.tdmconsult.ete.configuration.EnumRegistryConfiguration.EnumValue;
import com.tdmconsult.ete.masterdata.SecurityMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class EvaluationStateMapperService {

    private final EvaluationStateInfoService evaluationStateInfoService;

    public EvaluationStateDto toDto(final EvaluationState evaluation) {

        final var fiscalYear = evaluation.getFiscalYear();
        final var security = evaluation.getSecurity();

        final var answer = new EvaluationStateDto()
                .setSecurity(SecurityMapper.toDto(security))
                .setFiscalYearT0(fiscalYear)
                .setFiscalYearT1(fiscalYear - 1)
                .setTotalQuantity(evaluation.getTotalQuantity())
                .setMarketValue(evaluation.getEvaluationRate(fiscalYear)
                        .map(rate -> rate.multiply(evaluation.getTotalQuantity()))
                        .orElse(ZERO_AMOUNT))
                .setEvaluationRateT0(evaluation.getEvaluationRate(fiscalYear)
                        .map(r -> new EvaluationStateDto.RateDto()
                                .setDate(r.date())
                                .setType(r.type().name())
                                .setValue(r.value())).orElse(null))
                .setEvaluationTotalAmountT0(evaluation.getEvaluationTotal(fiscalYear))
                .setEvaluationRateT1(evaluation.getEvaluationRate(fiscalYear - 1)
                        .map(r -> new EvaluationStateDto.RateDto()
                                .setDate(r.date())
                                .setType(r.type().name())
                                .setValue(r.value())).orElse(null))
                .setEvaluationTotalAmountT1(evaluation.getEvaluationTotal(fiscalYear - 1))
                .setTotalBuyInFiscalYear(evaluation.getTotalBuyCost())
                .setTotalSellInFiscalYear(evaluation.getTotalSellCost())
                .setTotalProfitInFiscalYear(evaluation.getTotalProfit())
                .setTotalLossInFiscalYear(evaluation.getTotalLoss());

        // check if this security is/must be part of EOY processing in fiscal year
        final var hasTransactionsInFiscalYear = evaluation.getPositions().stream()
                .anyMatch(p -> p.getTradingDate().getYear() == fiscalYear);
        final var hasActivePosition = evaluation.getTotalQuantity() > 0;
        answer.setRequiresEoyProcessing(hasTransactionsInFiscalYear || hasActivePosition);

        answer.setBuySellPhrase(evaluationStateInfoService.getBuySellPhrase(evaluation));
        answer.setProfitLossPhrase(evaluationStateInfoService.getProfitLossPhrase(evaluation));
        answer.setEoyEvaluationPhrase(evaluationStateInfoService.getEoyEvaluationPhrase(evaluation));

        evaluation.getPositions().forEach(position -> {

            final var lineItem = new EvaluationStateDto.LineItem()
                    .setPublicId(position.getSource().getPublicId())
                    .setTradingDate(position.getTradingDate())
                    .setTransactionType(EnumValue.of(position.getPortfolioTransaction().getType()))
                    .setQuantity(position.getQuantity())
                    .setPrice(position.getPrice())
                    .setPrincipleAmount(position.getPrincipleAmount())
                    .setTotalCost(position.getTotalCost())
                    .setRemainingQuantityT0(position.at(fiscalYear).quantity())
                    .setEvaluatedAmountT0(position.getEvaluationAmount(fiscalYear))
                    .setEvaluationDeltaT0(position.getEvaluationDelta(fiscalYear))
                    .setRealizedPl(position.getProfitLoss());

            if (fiscalYear - 1 >= position.getTradingDate().getYear()) {
                lineItem
                        .setRemainingQuantityT1(position.at(fiscalYear - 1).quantity())
                        .setEvaluatedAmountT1(position.getEvaluationAmount(fiscalYear - 1))
                        .setEvaluationDeltaT1(position.getEvaluationDelta(fiscalYear - 1));
            }

            lineItem.setBucketChanges(position.getChange().stream()
                    .map(change -> new EvaluationStateDto.BucketChange()
                            .setOtherChangeAmount(change.otherChangeAmount())
                            .setOurChangeAmount(change.ourChangeAmount())
                            .setQuantityAfter(change.quantityAfter())
                            .setQuantityBefore(change.quantityBefore())
                            .setSourceDescription(
                                    change.other().getPortfolioTransaction().getType().getDisplayName()
                                            + " "
                                            + change.other().getQuantity()
                                            + "ST")
                            .setSourceTransactionPublicId(change.other().getPublicId()))
                    .toList());

            answer.getItems().add(lineItem);
        });

        // e.g. -64.979,05 € in 2024 - RWE AG
        final var evaluationTotalDeltaT0 = evaluation.getEvaluationTotalDelta(fiscalYear);
        answer.setEvaluationTotalDeltaT0(evaluationTotalDeltaT0);
        // e.g. -4.785,99 € € in 2023 - RWE AG
        final var evaluationTotalDeltaT1 = evaluation.getEvaluationTotalDelta(fiscalYear - 1);
        answer.setEvaluationTotalDeltaT1(evaluationTotalDeltaT1);

        answer.setEoyAppreciation(ZERO_AMOUNT);
        answer.setEoyAppreciationHr(ZERO_AMOUNT);
        answer.setEoyDepreciation(ZERO_AMOUNT);
        answer.setEoyDepreciationHr(ZERO_AMOUNT);

        final var deltaChange = evaluationTotalDeltaT0.subtract(evaluationTotalDeltaT1);
        final var hrResult = new EvaluationHrCalculator(evaluation).calculate();
        switch (hrResult.action()) {
            case VALUE_INCREASE_WITH_WRITE_UP -> {
                answer.setEoyAppreciation(deltaChange.abs());
                answer.setEoyAppreciationHr(hrResult.amount());
            }
            case VALUE_INCREASE_NO_WRITE_UP ->
                answer.setEoyAppreciation(deltaChange.abs());
            case VALUE_DECREASE_WITH_WRITE_DOWN -> {
                answer.setEoyDepreciation(deltaChange.abs());
                answer.setEoyDepreciationHr(hrResult.amount());
            }
            case VALUE_DECREASE_NO_WRITE_DOWN ->
                answer.setEoyDepreciation(deltaChange.abs());
            default -> {}
        }

        return answer;
    }
}