Skip to content

Commit

Permalink
start with #52
Browse files Browse the repository at this point in the history
  • Loading branch information
OlliL committed Apr 26, 2024
1 parent 26a04ba commit 2a2739d
Show file tree
Hide file tree
Showing 18 changed files with 373 additions and 23 deletions.
1 change: 1 addition & 0 deletions gen_mysqldump.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mysqldump -u root --set-gtid-purged=OFF --no-tablespaces --skip-triggers --quote
etf \
etfflows \
etfvalues \
etfpreliminarylumpsum \
moneyflows \
moneyflowsplitentries \
moneyflowreceipts \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,25 @@

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
public class EtfFlow extends AbstractEntity<EtfFlowID> {
private static final long serialVersionUID = 1L;
private EtfIsin isin;
private LocalDateTime time;
private BigDecimal amount;
private BigDecimal price;

public EtfFlow(final EtfFlow etfFlow) {
super.setId(etfFlow.getId());
this.setIsin(etfFlow.getIsin());
this.setAmount(etfFlow.getAmount());
this.setPrice(etfFlow.getPrice());
this.setTime(etfFlow.getTime());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.laladev.moneyjinn.model.etf;

import java.math.BigDecimal;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
public class EtfFlowWithTaxInfo extends EtfFlow {
private static final long serialVersionUID = 1L;
private BigDecimal accumulatedPreliminaryLumpSum;

public EtfFlowWithTaxInfo(final EtfFlow etfFlow) {
super(etfFlow);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// Copyright (c) 2024 Oliver Lehmann <[email protected]>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//

package org.laladev.moneyjinn.model.etf;

import java.math.BigDecimal;
import java.time.YearMonth;

import org.laladev.moneyjinn.model.AbstractEntity;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class EtfPreliminaryLumpSum extends AbstractEntity<EtfIsin> {
private static final long serialVersionUID = 1L;
private YearMonth yearMonth;
private BigDecimal amount;
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.laladev.moneyjinn.model.etf.EtfFlow;
import org.laladev.moneyjinn.model.etf.EtfFlowComparator;
import org.laladev.moneyjinn.model.etf.EtfFlowID;
import org.laladev.moneyjinn.model.etf.EtfFlowWithTaxInfo;
import org.laladev.moneyjinn.model.etf.EtfIsin;
import org.laladev.moneyjinn.model.etf.EtfValue;
import org.laladev.moneyjinn.model.setting.ClientCalcEtfSaleAskPrice;
Expand Down Expand Up @@ -103,7 +104,7 @@ public ResponseEntity<ListEtfOverviewResponse> listEtfOverview(
for (final Etf etf : etfs) {
final EtfValue etfValue = this.etfService.getEtfValueEndOfMonth(etf.getId(), requestYear, month);
final List<EtfFlow> allEtfFlows = this.etfService.getAllEtfFlowsUntil(etf.getId(), endOfMonth);
final List<EtfFlow> etfFlows = this.etfService.calculateEffectiveEtfFlows(allEtfFlows);
final List<EtfFlowWithTaxInfo> etfFlows = this.etfService.calculateEffectiveEtfFlows(allEtfFlows);
if (etfFlows != null && !etfFlows.isEmpty()) {
final EtfSummaryTransport transport = new EtfSummaryTransport();
transport.setIsin(etf.getId().getId());
Expand Down Expand Up @@ -145,7 +146,7 @@ public ResponseEntity<ListEtfFlowsResponse> listEtfFlows() {
for (final Etf etf : etfs) {
final List<EtfFlow> etfFlows = this.etfService.getAllEtfFlowsUntil(etf.getId(), LocalDateTime.now());
transports.addAll(super.mapList(etfFlows, EtfFlowTransport.class));
final List<EtfFlow> etfEffectiveFlows = new ArrayList<>(
final List<EtfFlowWithTaxInfo> etfEffectiveFlows = new ArrayList<>(
this.etfService.calculateEffectiveEtfFlows(etfFlows));
Collections.sort(etfEffectiveFlows, Collections.reverseOrder(new EtfFlowComparator()));
effectiveTransports.addAll(super.mapList(etfEffectiveFlows, EtfEffectiveFlowTransport.class));
Expand Down Expand Up @@ -188,19 +189,27 @@ public ResponseEntity<CalcEtfSaleResponse> calcEtfSale(@RequestBody final CalcEt
final EtfIsin etfIsin = new EtfIsin(request.getIsin());
BigDecimal openPieces = pieces;
BigDecimal originalBuyPrice = BigDecimal.ZERO;
BigDecimal overallPreliminaryLumpSum = BigDecimal.ZERO;

final List<EtfFlow> etfFlows = this.etfService.getAllEtfFlowsUntil(etfIsin, LocalDateTime.now());
final List<EtfFlow> effectiveEtfFlows = this.etfService.calculateEffectiveEtfFlows(etfFlows);
final List<EtfFlowWithTaxInfo> effectiveEtfFlows = new ArrayList<>(
this.etfService.calculateEffectiveEtfFlows(etfFlows));

if (effectiveEtfFlows != null && !effectiveEtfFlows.isEmpty()) {
for (final EtfFlow etfFlow : effectiveEtfFlows) {
for (final EtfFlowWithTaxInfo etfFlow : effectiveEtfFlows) {
BigDecimal useablePieces = etfFlow.getAmount();
if (useablePieces.compareTo(openPieces) > 0) {
useablePieces = openPieces;
overallPreliminaryLumpSum = overallPreliminaryLumpSum.add(etfFlow.getAccumulatedPreliminaryLumpSum()
.divide(etfFlow.getAmount(), 10, RoundingMode.HALF_UP).multiply(openPieces));
} else {
overallPreliminaryLumpSum = overallPreliminaryLumpSum
.add(etfFlow.getAccumulatedPreliminaryLumpSum());
}
openPieces = openPieces.subtract(useablePieces);
originalBuyPrice = originalBuyPrice.add(useablePieces.multiply(etfFlow.getPrice()));
}
overallPreliminaryLumpSum = overallPreliminaryLumpSum.setScale(2, RoundingMode.HALF_UP);
if (BigDecimal.ZERO.compareTo(openPieces) != 0) {
final ValidationResult validationResult = new ValidationResult();
validationResult.addValidationResultItem(new ValidationResultItem(null, ErrorCode.AMOUNT_TO_HIGH));
Expand All @@ -211,7 +220,9 @@ public ResponseEntity<CalcEtfSaleResponse> calcEtfSale(@RequestBody final CalcEt
final BigDecimal sellPrice = bidPrice.multiply(pieces);
final BigDecimal transactionCosts = request.getTransactionCosts().multiply(BigDecimal.valueOf(2));
final BigDecimal profit = sellPrice.subtract(originalBuyPrice);
final BigDecimal chargeable = profit.multiply(TAX_RELEVANT_PERCENTAGE).setScale(2, RoundingMode.UP);
final BigDecimal chargeable = profit.multiply(TAX_RELEVANT_PERCENTAGE).setScale(2, RoundingMode.UP)
.subtract(overallPreliminaryLumpSum.multiply(TAX_RELEVANT_PERCENTAGE).setScale(2,
RoundingMode.UP));
final BigDecimal rebuyLosses = newBuyPrice.subtract(sellPrice);
final BigDecimal overallCosts = rebuyLosses.add(transactionCosts);
response.setNewBuyPrice(newBuyPrice);
Expand All @@ -221,6 +232,7 @@ public ResponseEntity<CalcEtfSaleResponse> calcEtfSale(@RequestBody final CalcEt
response.setPieces(pieces);
response.setOriginalBuyPrice(originalBuyPrice);
response.setProfit(profit);
response.setAccumulatedPreliminaryLumpSum(overallPreliminaryLumpSum);
response.setChargeable(chargeable);
response.setRebuyLosses(rebuyLosses);
response.setOverallCosts(overallCosts);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.laladev.moneyjinn.converter.config.MapStructConfig;
import org.laladev.moneyjinn.converter.javatypes.LocalDateTimeToOffsetDateTimeMapper;
import org.laladev.moneyjinn.model.etf.EtfFlow;
import org.laladev.moneyjinn.model.etf.EtfFlowWithTaxInfo;
import org.laladev.moneyjinn.server.model.EtfEffectiveFlowTransport;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
Expand All @@ -40,17 +41,19 @@

@Mapper(config = MapStructConfig.class, uses = { EtfIsinMapper.class, EtfFlowIdMapper.class,
LocalDateTimeToOffsetDateTimeMapper.class })
public interface EtfEffectiveFlowTransportMapper extends IMapstructMapper<EtfFlow, EtfEffectiveFlowTransport> {
public interface EtfEffectiveFlowTransportMapper
extends IMapstructMapper<EtfFlowWithTaxInfo, EtfEffectiveFlowTransport> {
@Override
@Mapping(target = "id", source = "etfflowid")
@Mapping(target = "time", source = "timestamp")
EtfFlow mapBToA(EtfEffectiveFlowTransport etfEffectiveFlowTransport);
@Mapping(target = "accumulatedPreliminaryLumpSum", ignore = true)
EtfFlowWithTaxInfo mapBToA(EtfEffectiveFlowTransport etfEffectiveFlowTransport);

@Override
@Mapping(target = "etfflowid", source = "id")
@Mapping(target = "nanoseconds", source = "time.nano")
@Mapping(target = "timestamp", source = "time")
EtfEffectiveFlowTransport mapAToB(EtfFlow etfFlow);
EtfEffectiveFlowTransport mapAToB(EtfFlowWithTaxInfo etfFlow);

@AfterMapping
default void doAfterMapping(final EtfEffectiveFlowTransport source, @MappingTarget final EtfFlow entity) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"sellPrice",
"newBuyPrice",
"profit",
"accumulatedPreliminaryLumpSum",
"chargeable",
"transactionCosts",
"rebuyLosses",
Expand All @@ -28,6 +29,9 @@
"profit": {
"type": "number"
},
"accumulatedPreliminaryLumpSum": {
"type": "number"
},
"chargeable": {
"type": "number"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ void test_standardRequest_FullResponseObject() throws Exception {
expected.setRebuyLosses(new BigDecimal("5.000"));
expected.setOverallCosts(new BigDecimal("6.980"));
expected.setPieces(BigDecimal.TEN);
expected.accumulatedPreliminaryLumpSum(BigDecimal.ZERO.setScale(2));

final CalcEtfSaleResponse actual = super.callUsecaseExpect200(request, CalcEtfSaleResponse.class);

Expand Down Expand Up @@ -93,6 +94,7 @@ void test_negativeInput_AbsoluteValuesAreUsed() throws Exception {
expected.setRebuyLosses(new BigDecimal("5.000"));
expected.setOverallCosts(new BigDecimal("6.980"));
expected.setPieces(BigDecimal.TEN);
expected.accumulatedPreliminaryLumpSum(BigDecimal.ZERO.setScale(2));

final CalcEtfSaleResponse actual = super.callUsecaseExpect200(request, CalcEtfSaleResponse.class);

Expand Down
1 change: 1 addition & 0 deletions moneyjinn-server/src/test/resources/h2defaults.sql
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ DELETE FROM `monthlysettlements`;
DELETE FROM `moneyflowreceipts`;
DELETE FROM `moneyflowsplitentries`;
DELETE FROM `moneyflows`;
DELETE FROM `etfpreliminarylumpsum`;
DELETE FROM `etfvalues`;
DELETE FROM `etfflows`;
DELETE FROM `etf`;
Expand Down
16 changes: 16 additions & 0 deletions moneyjinn-server/src/test/resources/h2dump.sql
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,22 @@ CREATE TABLE `etfvalues` (
);
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Table structure for table `etfpreliminarylumpsum`
--

DROP TABLE IF EXISTS `etfpreliminarylumpsum`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `etfpreliminarylumpsum` (
`isin` varchar(30) NOT NULL,
`year` year NOT NULL,
`month` int unsigned NOT NULL,
`amount` decimal(8,2) NOT NULL,
PRIMARY KEY (`isin`,`year`,`month`)
);
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Table structure for table `moneyflows`
--
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.laladev.moneyjinn.model.etf.Etf;
import org.laladev.moneyjinn.model.etf.EtfFlow;
import org.laladev.moneyjinn.model.etf.EtfFlowID;
import org.laladev.moneyjinn.model.etf.EtfFlowWithTaxInfo;
import org.laladev.moneyjinn.model.etf.EtfIsin;
import org.laladev.moneyjinn.model.etf.EtfValue;
import org.laladev.moneyjinn.model.validation.ValidationResult;
Expand All @@ -54,5 +55,5 @@ public interface IEtfService {

void deleteEtfFlow(EtfFlowID etfFlowId);

List<EtfFlow> calculateEffectiveEtfFlows(List<EtfFlow> etfFlows);
List<EtfFlowWithTaxInfo> calculateEffectiveEtfFlows(List<EtfFlow> etfFlows);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

import org.laladev.moneyjinn.service.dao.data.EtfData;
import org.laladev.moneyjinn.service.dao.data.EtfFlowData;
import org.laladev.moneyjinn.service.dao.data.EtfPreliminaryLumpSumData;
import org.laladev.moneyjinn.service.dao.data.EtfValueData;
import org.laladev.moneyjinn.service.dao.mapper.IEtfDaoMapper;

Expand Down Expand Up @@ -80,4 +81,8 @@ public void updateEtfFlow(final EtfFlowData etfFlowData) {
public void deleteEtfFlow(final Long etfFlowId) {
this.mapper.deleteEtfFlow(etfFlowId);
}

public List<EtfPreliminaryLumpSumData> getAllPreliminaryLumpSum(final String isin) {
return this.mapper.getAllPreliminaryLumpSum(isin);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// Copyright (c) 2024 Oliver Lehmann <[email protected]>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//

package org.laladev.moneyjinn.service.dao.data;

import java.math.BigDecimal;

import lombok.Data;

@Data
public class EtfPreliminaryLumpSumData {
private String isin;
private Integer month;
private Integer year;
private BigDecimal amount;
}
Loading

0 comments on commit 2a2739d

Please sign in to comment.