From c8316ec5934a7b6959b5e09998ce5e548cb422ea Mon Sep 17 00:00:00 2001 From: vananiev Date: Thu, 4 May 2023 00:13:29 +0300 Subject: [PATCH 01/17] add checker framework and unit tests github action --- .github/workflows/unit-tests.yml | 45 ++++++++ pom.xml | 104 ++++++++++++++++++ .../investbook/InvestbookApplicationTest.java | 3 +- .../parser/psb/CashFlowTableTest.java | 16 +-- .../psb/CouponAndAmortizationTableTest.java | 18 +-- .../psb/DerivativeCashFlowTableTest.java | 16 +-- .../psb/DerivativeTransactionTableTest.java | 18 +-- .../parser/psb/DividendTableTest.java | 18 +-- .../parser/psb/PortfolioCashTableTest.java | 15 +-- .../psb/PortfolioSecuritiesTableTest.java | 18 +-- .../parser/psb/ReportParserServiceTest.java | 19 ++-- .../parser/psb/TransactionTableTest.java | 18 +-- .../moex/MoexDerivativeCodeServiceTest.java | 4 +- 13 files changed, 230 insertions(+), 82 deletions(-) create mode 100644 .github/workflows/unit-tests.yml rename src/test/java/ru/{portfolio/portfolio => investbook}/parser/psb/CashFlowTableTest.java (84%) rename src/test/java/ru/{portfolio/portfolio => investbook}/parser/psb/CouponAndAmortizationTableTest.java (81%) rename src/test/java/ru/{portfolio/portfolio => investbook}/parser/psb/DerivativeCashFlowTableTest.java (84%) rename src/test/java/ru/{portfolio/portfolio => investbook}/parser/psb/DerivativeTransactionTableTest.java (81%) rename src/test/java/ru/{portfolio/portfolio => investbook}/parser/psb/DividendTableTest.java (80%) rename src/test/java/ru/{portfolio/portfolio => investbook}/parser/psb/PortfolioCashTableTest.java (80%) rename src/test/java/ru/{portfolio/portfolio => investbook}/parser/psb/PortfolioSecuritiesTableTest.java (79%) rename src/test/java/ru/{portfolio/portfolio => investbook}/parser/psb/ReportParserServiceTest.java (79%) rename src/test/java/ru/{portfolio/portfolio => investbook}/parser/psb/TransactionTableTest.java (81%) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 00000000..82042fba --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,45 @@ +name: Unit Tests + +on: + workflow_dispatch: + pull_request: + branches: + - 'master' + - 'develop' + push: + branches: + - 'master' + - 'develop' + +jobs: + tests: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: '20' + distribution: 'liberica' + cache: maven + + - name: Maven Tests + run: mvn --batch-mode clean test + + - name: Test Coverage + uses: codecov/codecov-action@v3 + + - name: SonarCloud Analyze + run: > + mvn --batch-mode sonar:sonar + -Dsonar.projectKey=spacious-team_investbook + -Dsonar.organization=spacious-team + -Dsonar.host.url=https://sonarcloud.io + -Dsonar.login=$SONAR_TOKEN + -Dsonar.coverage.jacoco.xmlReportPaths=./target/site/jacoco/jacoco.xml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/pom.xml b/pom.xml index bc5b42a7..2fe02645 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,8 @@ 24.2 22 + 1.18.34 + 3.34.0 @@ -209,6 +211,7 @@ org.projectlombok lombok + ${lombok.version} provided true @@ -222,10 +225,91 @@ poi-scratchpad 5.2.5 + + org.checkerframework + checker-qual + ${checkerframework.version} + provided + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-junit-jupiter + test + + + nl.jqno.equalsverifier + equalsverifier + 3.14.1 + test + + + + + maven-surefire-plugin + 2.22.2 + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + true + true + true + + + org.projectlombok + lombok + ${lombok.version} + + + org.hibernate + hibernate-jpamodelgen + ${hibernate.version} + + + org.checkerframework + checker + ${checkerframework.version} + + + + + lombok.launch.AnnotationProcessorHider$AnnotationProcessor + + + org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor + + + org.checkerframework.checker.nullness.NullnessChecker + + + + -Awarns + -AskipDefs=.*Test + -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED + + + org.springframework.boot spring-boot-maven-plugin @@ -287,6 +371,26 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.9 + + + prepare-agent + + prepare-agent + + + + report + test + + report + + + + diff --git a/src/test/java/ru/investbook/InvestbookApplicationTest.java b/src/test/java/ru/investbook/InvestbookApplicationTest.java index a335e000..f9b19c0b 100644 --- a/src/test/java/ru/investbook/InvestbookApplicationTest.java +++ b/src/test/java/ru/investbook/InvestbookApplicationTest.java @@ -29,11 +29,11 @@ import org.springframework.context.ApplicationContext; import org.springframework.test.web.servlet.MockMvc; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.testng.Assert.assertNotNull; @SpringBootTest(classes = InvestbookApplication.class) @AutoConfigureMockMvc @@ -65,5 +65,4 @@ void shouldHaveActuatorHandler(String actuatorPath, String jsonPath, String expe .andExpect(status().isOk()) .andExpect(jsonPath("$." + jsonPath).value(expected)); } - } diff --git a/src/test/java/ru/portfolio/portfolio/parser/psb/CashFlowTableTest.java b/src/test/java/ru/investbook/parser/psb/CashFlowTableTest.java similarity index 84% rename from src/test/java/ru/portfolio/portfolio/parser/psb/CashFlowTableTest.java rename to src/test/java/ru/investbook/parser/psb/CashFlowTableTest.java index d8fc85bd..a2afee8b 100644 --- a/src/test/java/ru/portfolio/portfolio/parser/psb/CashFlowTableTest.java +++ b/src/test/java/ru/investbook/parser/psb/CashFlowTableTest.java @@ -18,32 +18,32 @@ package ru.investbook.parser.psb; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.spacious_team.broker.pojo.EventCashFlow; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Ignore; -import org.testng.annotations.Test; import ru.investbook.parser.SecurityRegistrar; import java.io.IOException; import java.math.BigDecimal; import java.util.List; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Ignore +@Disabled public class CashFlowTableTest { @Mock SecurityRegistrar securityRegistrar; - @DataProvider(name = "cash") - Object[][] getData() { + static Object[][] cash() { return new Object[][]{{"E:\\1.xlsx", BigDecimal.valueOf(400 - 760.77)}, {"E:\\Налог.xlsx", BigDecimal.valueOf(-542.0)}}; } - @Test(dataProvider = "cash") + @ParameterizedTest + @MethodSource("cash") void testIsin(String reportFile, BigDecimal expectedCashIn) throws IOException { PsbBrokerReport report = new PsbBrokerReport(reportFile, securityRegistrar); List data = new CashFlowTable(report).getData(); diff --git a/src/test/java/ru/portfolio/portfolio/parser/psb/CouponAndAmortizationTableTest.java b/src/test/java/ru/investbook/parser/psb/CouponAndAmortizationTableTest.java similarity index 81% rename from src/test/java/ru/portfolio/portfolio/parser/psb/CouponAndAmortizationTableTest.java rename to src/test/java/ru/investbook/parser/psb/CouponAndAmortizationTableTest.java index e6a49393..ff97f4d9 100644 --- a/src/test/java/ru/portfolio/portfolio/parser/psb/CouponAndAmortizationTableTest.java +++ b/src/test/java/ru/investbook/parser/psb/CouponAndAmortizationTableTest.java @@ -1,6 +1,6 @@ /* * InvestBook - * Copyright (C) 2020 Spacious Team + * Copyright (C) 2023 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,30 +18,30 @@ package ru.investbook.parser.psb; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.spacious_team.broker.pojo.SecurityEventCashFlow; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Ignore; -import org.testng.annotations.Test; import ru.investbook.parser.SecurityRegistrar; import java.io.IOException; import java.util.List; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Ignore +@Disabled public class CouponAndAmortizationTableTest { @Mock SecurityRegistrar securityRegistrar; - @DataProvider(name = "isin") - Object[][] getData() { + static Object[][] isin() { return new Object[][] {{"E:\\1.xlsx", "RU000A0ZYAQ7", "RU000A0JV3M2" }}; } - @Test(dataProvider = "isin") + @ParameterizedTest + @MethodSource("isin") void testIsin(String report, String firstIsin, String lastIsin) throws IOException { PsbBrokerReport psbBrokerReport = new PsbBrokerReport(report, securityRegistrar); List data = new CouponAmortizationRedemptionTable(psbBrokerReport).getData(); diff --git a/src/test/java/ru/portfolio/portfolio/parser/psb/DerivativeCashFlowTableTest.java b/src/test/java/ru/investbook/parser/psb/DerivativeCashFlowTableTest.java similarity index 84% rename from src/test/java/ru/portfolio/portfolio/parser/psb/DerivativeCashFlowTableTest.java rename to src/test/java/ru/investbook/parser/psb/DerivativeCashFlowTableTest.java index 133ba54f..a2dd0b71 100644 --- a/src/test/java/ru/portfolio/portfolio/parser/psb/DerivativeCashFlowTableTest.java +++ b/src/test/java/ru/investbook/parser/psb/DerivativeCashFlowTableTest.java @@ -18,31 +18,31 @@ package ru.investbook.parser.psb; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.spacious_team.broker.pojo.SecurityEventCashFlow; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Ignore; -import org.testng.annotations.Test; import ru.investbook.parser.SecurityRegistrar; import java.io.IOException; import java.math.BigDecimal; import java.util.List; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Ignore +@Disabled public class DerivativeCashFlowTableTest { @Mock SecurityRegistrar securityRegistrar; - @DataProvider(name = "cash-flow") - Object[][] getData() { + static Object[][] cashFlow() { return new Object[][] {{"E:\\Исполнение фьючерса.xlsx", BigDecimal.valueOf(-733.0) }}; } - @Test(dataProvider = "cash-flow") + @ParameterizedTest + @MethodSource("cashFlow") void testIsin(String report, BigDecimal expectedSum) throws IOException { PsbBrokerReport psbBrokerReport = new PsbBrokerReport(report, securityRegistrar); List data = new DerivativeCashFlowTable(psbBrokerReport).getData(); diff --git a/src/test/java/ru/portfolio/portfolio/parser/psb/DerivativeTransactionTableTest.java b/src/test/java/ru/investbook/parser/psb/DerivativeTransactionTableTest.java similarity index 81% rename from src/test/java/ru/portfolio/portfolio/parser/psb/DerivativeTransactionTableTest.java rename to src/test/java/ru/investbook/parser/psb/DerivativeTransactionTableTest.java index 383f677b..e9cb2ba4 100644 --- a/src/test/java/ru/portfolio/portfolio/parser/psb/DerivativeTransactionTableTest.java +++ b/src/test/java/ru/investbook/parser/psb/DerivativeTransactionTableTest.java @@ -1,6 +1,6 @@ /* * InvestBook - * Copyright (C) 2021 Spacious Team + * Copyright (C) 2023 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,30 +18,30 @@ package ru.investbook.parser.psb; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.spacious_team.broker.report_parser.api.DerivativeTransaction; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Ignore; -import org.testng.annotations.Test; import ru.investbook.parser.SecurityRegistrar; import java.io.IOException; import java.util.List; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Ignore +@Disabled public class DerivativeTransactionTableTest { @Mock SecurityRegistrar securityRegistrar; - @DataProvider(name = "isin") - Object[][] getData() { + static Object[][] isin() { return new Object[][] {{"E:\\Исполнение фьючерса.xlsx", "Si-12.19M191219CA65500", "Si-12.19" }}; } - @Test(dataProvider = "isin") + @ParameterizedTest + @MethodSource("isin") void testIsin(String report, String firstIsin, String lastIsin) throws IOException { PsbBrokerReport psbBrokerReport = new PsbBrokerReport(report, securityRegistrar); List data = new DerivativeTransactionTable(psbBrokerReport).getData(); diff --git a/src/test/java/ru/portfolio/portfolio/parser/psb/DividendTableTest.java b/src/test/java/ru/investbook/parser/psb/DividendTableTest.java similarity index 80% rename from src/test/java/ru/portfolio/portfolio/parser/psb/DividendTableTest.java rename to src/test/java/ru/investbook/parser/psb/DividendTableTest.java index 5959f0de..899eacbb 100644 --- a/src/test/java/ru/portfolio/portfolio/parser/psb/DividendTableTest.java +++ b/src/test/java/ru/investbook/parser/psb/DividendTableTest.java @@ -1,6 +1,6 @@ /* * InvestBook - * Copyright (C) 2020 Spacious Team + * Copyright (C) 2023 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,30 +18,30 @@ package ru.investbook.parser.psb; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.spacious_team.broker.pojo.SecurityEventCashFlow; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Ignore; -import org.testng.annotations.Test; import ru.investbook.parser.SecurityRegistrar; import java.io.IOException; import java.util.List; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Ignore +@Disabled public class DividendTableTest { @Mock SecurityRegistrar securityRegistrar; - @DataProvider(name = "isin") - Object[][] getData() { + static Object[][] getData() { return new Object[][] {{"E:\\1.xlsx", "RU000A0JRKT8", "RU000A0JRKT8" }}; } - @Test(dataProvider = "isin") + @ParameterizedTest + @MethodSource("cashFisinlow") void testIsin(String report, String firstIsin, String lastIsin) throws IOException { PsbBrokerReport psbBrokerReport = new PsbBrokerReport(report, securityRegistrar); List data = new DividendTable(psbBrokerReport).getData(); diff --git a/src/test/java/ru/portfolio/portfolio/parser/psb/PortfolioCashTableTest.java b/src/test/java/ru/investbook/parser/psb/PortfolioCashTableTest.java similarity index 80% rename from src/test/java/ru/portfolio/portfolio/parser/psb/PortfolioCashTableTest.java rename to src/test/java/ru/investbook/parser/psb/PortfolioCashTableTest.java index 4768c0f7..8aa4a7bf 100644 --- a/src/test/java/ru/portfolio/portfolio/parser/psb/PortfolioCashTableTest.java +++ b/src/test/java/ru/investbook/parser/psb/PortfolioCashTableTest.java @@ -1,6 +1,6 @@ /* * InvestBook - * Copyright (C) 2020 Spacious Team + * Copyright (C) 2023 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,18 +18,18 @@ package ru.investbook.parser.psb; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Ignore; -import org.testng.annotations.Test; import ru.investbook.parser.SecurityRegistrar; import java.io.IOException; import java.math.BigDecimal; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Ignore +@Disabled public class PortfolioCashTableTest { @Mock @@ -40,7 +40,8 @@ Object[][] getData() { return new Object[][] {{"E:\\1.xlsx", BigDecimal.valueOf(350.37)}}; } - @Test(dataProvider = "cash_in") + @ParameterizedTest + @MethodSource("cashIn") void testIsin(String report, BigDecimal expectedCash) throws IOException { PsbBrokerReport psbBrokerReport = new PsbBrokerReport(report, securityRegistrar); assertEquals(new CashTable(psbBrokerReport).getData().get(0).getValue(), expectedCash); diff --git a/src/test/java/ru/portfolio/portfolio/parser/psb/PortfolioSecuritiesTableTest.java b/src/test/java/ru/investbook/parser/psb/PortfolioSecuritiesTableTest.java similarity index 79% rename from src/test/java/ru/portfolio/portfolio/parser/psb/PortfolioSecuritiesTableTest.java rename to src/test/java/ru/investbook/parser/psb/PortfolioSecuritiesTableTest.java index bbca4142..13f550ab 100644 --- a/src/test/java/ru/portfolio/portfolio/parser/psb/PortfolioSecuritiesTableTest.java +++ b/src/test/java/ru/investbook/parser/psb/PortfolioSecuritiesTableTest.java @@ -1,6 +1,6 @@ /* * InvestBook - * Copyright (C) 2020 Spacious Team + * Copyright (C) 2023 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,30 +18,30 @@ package ru.investbook.parser.psb; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.spacious_team.broker.pojo.Security; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Ignore; -import org.testng.annotations.Test; import ru.investbook.parser.SecurityRegistrar; import java.io.IOException; import java.util.List; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Ignore +@Disabled public class PortfolioSecuritiesTableTest { @Mock SecurityRegistrar securityRegistrar; - @DataProvider(name = "isin") - Object[][] getData() { + static Object[][] isin() { return new Object[][] {{"E:\\1.xlsx", "RU000A0ZZDQ8", "RU000A0JV7J9" }}; } - @Test(dataProvider = "isin") + @ParameterizedTest + @MethodSource("isin") void testIsin(String report, String firstIsin, String lastIsin) throws IOException { List data = new SecuritiesTable(new PsbBrokerReport(report, securityRegistrar)).getData(); assertEquals(data.get(0).getId(), firstIsin); diff --git a/src/test/java/ru/portfolio/portfolio/parser/psb/ReportParserServiceTest.java b/src/test/java/ru/investbook/parser/psb/ReportParserServiceTest.java similarity index 79% rename from src/test/java/ru/portfolio/portfolio/parser/psb/ReportParserServiceTest.java rename to src/test/java/ru/investbook/parser/psb/ReportParserServiceTest.java index 2713b45c..ddd1682a 100644 --- a/src/test/java/ru/portfolio/portfolio/parser/psb/ReportParserServiceTest.java +++ b/src/test/java/ru/investbook/parser/psb/ReportParserServiceTest.java @@ -1,6 +1,6 @@ /* * InvestBook - * Copyright (C) 2020 Spacious Team + * Copyright (C) 2023 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,13 +18,12 @@ package ru.investbook.parser.psb; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Ignore; -import org.testng.annotations.Test; import ru.investbook.InvestbookApplication; import ru.investbook.parser.ReportParserService; import ru.investbook.parser.SecurityRegistrar; @@ -32,9 +31,9 @@ import java.io.IOException; import java.nio.file.Paths; -@Ignore +@Disabled @SpringBootTest(classes = InvestbookApplication.class) -public class ReportParserServiceTest extends AbstractTestNGSpringContextTests { +public class ReportParserServiceTest { @Mock SecurityRegistrar securityRegistrar; @@ -42,15 +41,15 @@ public class ReportParserServiceTest extends AbstractTestNGSpringContextTests { @Autowired private ReportParserService reportParserService; - @DataProvider(name = "report") - Object[][] getData() { + static Object[][] report() { return new Object[][] {{"E:\\1.xlsx"}, {"E:\\2.xlsx"}, {"E:\\Исполнение фьючерса.xlsx"}, {"E:\\Налог.xlsx"}}; } - @Test(dataProvider = "report") + @ParameterizedTest + @MethodSource("report") void testParse(String report) throws IOException { PsbBrokerReport brokerReport = new PsbBrokerReport(Paths.get(report), securityRegistrar); PsbReportTables reportTableFactory = new PsbReportTables(brokerReport, null); diff --git a/src/test/java/ru/portfolio/portfolio/parser/psb/TransactionTableTest.java b/src/test/java/ru/investbook/parser/psb/TransactionTableTest.java similarity index 81% rename from src/test/java/ru/portfolio/portfolio/parser/psb/TransactionTableTest.java rename to src/test/java/ru/investbook/parser/psb/TransactionTableTest.java index facb336e..be20e3bf 100644 --- a/src/test/java/ru/portfolio/portfolio/parser/psb/TransactionTableTest.java +++ b/src/test/java/ru/investbook/parser/psb/TransactionTableTest.java @@ -1,6 +1,6 @@ /* * InvestBook - * Copyright (C) 2020 Spacious Team + * Copyright (C) 2023 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,30 +18,30 @@ package ru.investbook.parser.psb; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.spacious_team.broker.report_parser.api.SecurityTransaction; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Ignore; -import org.testng.annotations.Test; import ru.investbook.parser.SecurityRegistrar; import java.io.IOException; import java.util.List; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Ignore +@Disabled public class TransactionTableTest { @Mock SecurityRegistrar securityRegistrar; - @DataProvider(name = "isin") - Object[][] getData() { + static Object[][] isin() { return new Object[][] {{"E:\\1.xlsx", "RU000A0ZZYP6", "RU000A0JV4L2" }}; } - @Test(dataProvider = "isin") + @ParameterizedTest + @MethodSource("isin") void testIsin(String report, String firstIsin, String lastIsin) throws IOException { PsbBrokerReport psbBrokerReport = new PsbBrokerReport(report, securityRegistrar); List data = new SecurityTransactionTable(psbBrokerReport).getData(); diff --git a/src/test/java/ru/investbook/service/moex/MoexDerivativeCodeServiceTest.java b/src/test/java/ru/investbook/service/moex/MoexDerivativeCodeServiceTest.java index ebe17dd5..bbcf8fff 100644 --- a/src/test/java/ru/investbook/service/moex/MoexDerivativeCodeServiceTest.java +++ b/src/test/java/ru/investbook/service/moex/MoexDerivativeCodeServiceTest.java @@ -26,8 +26,8 @@ import java.util.Optional; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(MockitoExtension.class) public class MoexDerivativeCodeServiceTest { From 91b072c1fcce7c250da2ffef0e65ca0eb8fafa97 Mon Sep 17 00:00:00 2001 From: vananiev Date: Thu, 4 May 2023 00:25:22 +0300 Subject: [PATCH 02/17] add tests badge to README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 48c62cc5..32e1d2bd 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ [![spring-boot-version](https://img.shields.io/badge/spring--boot-3.3.4-brightgreen?style=flat-square)](https://github.com/spring-projects/spring-boot/releases) [![hits-of-code](https://img.shields.io/badge/dynamic/json?style=flat-square&color=lightblue&label=hits-of-code&url=https://hitsofcode.com/github/spacious-team/investbook/json?branch=develop&query=$.count)](https://hitsofcode.com/github/spacious-team/investbook/view?branch=develop) [![github-closed-pull-requests](https://img.shields.io/github/issues-pr-closed/spacious-team/investbook?style=flat-square&color=brightgreen)](https://github.com/spacious-team/investbook/pulls?q=is%3Apr+is%3Aclosed) +[![Unit tests](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fspacious-team%2Finvestbook%2Fbadge%3Fref%3Ddevelop&style=flat-square&label=test&logo=none)]( +https://github.com/spacious-team/investbook/actions/workflows/unit-tests.yml) [![github-workflow-status](https://img.shields.io/github/actions/workflow/status/spacious-team/investbook/publish-docker.yml?style=flat-square&branch=master)](https://github.com/spacious-team/investbook/actions/workflows/publish-docker.yml) [![github-all-releases](https://img.shields.io/github/downloads/spacious-team/investbook/total?style=flat-square&logo=github&color=lightblue)](https://github.com/spacious-team/investbook/releases/latest) [![docker-pulls](https://img.shields.io/docker/pulls/spaciousteam/investbook?style=flat-square&logo=docker&color=lightblue&logoColor=white)](https://hub.docker.com/r/spaciousteam/investbook) From 314bd2570af95e8d384cafbd42308248af187af2 Mon Sep 17 00:00:00 2001 From: vananiev Date: Thu, 4 May 2023 00:38:26 +0300 Subject: [PATCH 03/17] add package-info.java --- src/main/java/ru/investbook/package-info.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/ru/investbook/package-info.java diff --git a/src/main/java/ru/investbook/package-info.java b/src/main/java/ru/investbook/package-info.java new file mode 100644 index 00000000..b2d30b15 --- /dev/null +++ b/src/main/java/ru/investbook/package-info.java @@ -0,0 +1,23 @@ +/* + * Table Wrapper Xml SpreadsheetML Impl + * Copyright (C) 2022 Spacious Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +@DefaultQualifier(NonNull.class) +package ru.investbook; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; From 94604ab97c3d083f42ab79a5d03563b9c5d7f50b Mon Sep 17 00:00:00 2001 From: vananiev Date: Fri, 5 May 2023 00:58:16 +0300 Subject: [PATCH 04/17] add Nullable to entity classes --- .../java/ru/investbook/entity/EventCashFlowEntity.java | 4 +++- src/main/java/ru/investbook/entity/IssuerEntity.java | 4 +++- .../ru/investbook/entity/SecurityDescriptionEntity.java | 5 +++-- src/main/java/ru/investbook/entity/SecurityEntity.java | 8 +++++--- .../java/ru/investbook/entity/SecurityQuoteEntity.java | 8 +++++--- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/ru/investbook/entity/EventCashFlowEntity.java b/src/main/java/ru/investbook/entity/EventCashFlowEntity.java index 5301285d..7136634b 100644 --- a/src/main/java/ru/investbook/entity/EventCashFlowEntity.java +++ b/src/main/java/ru/investbook/entity/EventCashFlowEntity.java @@ -29,6 +29,8 @@ import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.annotations.GenericGenerator; import java.math.BigDecimal; import java.time.Instant; @@ -68,5 +70,5 @@ public class EventCashFlowEntity { @Basic @Column(name = "description") - private String description; + private @Nullable String description; } diff --git a/src/main/java/ru/investbook/entity/IssuerEntity.java b/src/main/java/ru/investbook/entity/IssuerEntity.java index 646cb621..9b811c41 100644 --- a/src/main/java/ru/investbook/entity/IssuerEntity.java +++ b/src/main/java/ru/investbook/entity/IssuerEntity.java @@ -23,6 +23,8 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; import lombok.Data; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.annotations.GenericGenerator; @Entity @Table(name = "issuer") @@ -34,7 +36,7 @@ public class IssuerEntity { private Integer id; @Column(name = "taxpayer_id") - private String taxpayerId; + private @Nullable String taxpayerId; @Column(name = "name", nullable = false) private String name; diff --git a/src/main/java/ru/investbook/entity/SecurityDescriptionEntity.java b/src/main/java/ru/investbook/entity/SecurityDescriptionEntity.java index 4fbd73dd..10a840ca 100644 --- a/src/main/java/ru/investbook/entity/SecurityDescriptionEntity.java +++ b/src/main/java/ru/investbook/entity/SecurityDescriptionEntity.java @@ -28,6 +28,7 @@ import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; +import org.checkerframework.checker.nullness.qual.Nullable; @Entity @Table(name = "security_description") @@ -39,10 +40,10 @@ public class SecurityDescriptionEntity { private int security; @Column(name = "sector") - private String sector; + private @Nullable String sector; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "issuer", referencedColumnName = "id") @JsonIgnoreProperties({"hibernateLazyInitializer"}) - private IssuerEntity issuer; + private @Nullable IssuerEntity issuer; } diff --git a/src/main/java/ru/investbook/entity/SecurityEntity.java b/src/main/java/ru/investbook/entity/SecurityEntity.java index e130459d..6ce5564d 100644 --- a/src/main/java/ru/investbook/entity/SecurityEntity.java +++ b/src/main/java/ru/investbook/entity/SecurityEntity.java @@ -26,6 +26,8 @@ import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.annotations.GenericGenerator; import org.spacious_team.broker.pojo.SecurityType; import java.util.regex.Pattern; @@ -48,11 +50,11 @@ public class SecurityEntity { private SecurityType type; @Column(name = "isin") - private String isin; + private @Nullable String isin; @Column(name = "ticker") - private String ticker; + private @Nullable String ticker; @Column(name = "name") - private String name; + private @Nullable String name; } diff --git a/src/main/java/ru/investbook/entity/SecurityQuoteEntity.java b/src/main/java/ru/investbook/entity/SecurityQuoteEntity.java index 1e4038e3..7588b7ab 100644 --- a/src/main/java/ru/investbook/entity/SecurityQuoteEntity.java +++ b/src/main/java/ru/investbook/entity/SecurityQuoteEntity.java @@ -28,6 +28,8 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.annotations.GenericGenerator; import java.math.BigDecimal; import java.time.Instant; @@ -57,13 +59,13 @@ public class SecurityQuoteEntity { @Basic @Column(name = "price") - private BigDecimal price; + private @Nullable BigDecimal price; @Basic @Column(name = "accrued_interest") - private BigDecimal accruedInterest; + private @Nullable BigDecimal accruedInterest; @Basic @Column(name = "currency") - private String currency; + private @Nullable String currency; } From add856b294041420914505b59a79dee82fa7832f Mon Sep 17 00:00:00 2001 From: vananiev Date: Wed, 3 Jan 2024 18:02:47 +0300 Subject: [PATCH 05/17] fix compilation error --- pom.xml | 6 +++--- .../java/ru/investbook/parser/psb/DividendTableTest.java | 4 +++- .../ru/investbook/parser/psb/PortfolioCashTableTest.java | 3 +-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 2fe02645..ccbb8029 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 24.2 22 1.18.34 - 3.34.0 + 3.42.0 @@ -288,9 +288,9 @@ lombok.launch.AnnotationProcessorHider$AnnotationProcessor - + org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor - + org.checkerframework.checker.nullness.NullnessChecker diff --git a/src/test/java/ru/investbook/parser/psb/DividendTableTest.java b/src/test/java/ru/investbook/parser/psb/DividendTableTest.java index 899eacbb..34a56893 100644 --- a/src/test/java/ru/investbook/parser/psb/DividendTableTest.java +++ b/src/test/java/ru/investbook/parser/psb/DividendTableTest.java @@ -36,7 +36,7 @@ public class DividendTableTest { @Mock SecurityRegistrar securityRegistrar; - static Object[][] getData() { + static Object[][] cashFisinlow() { return new Object[][] {{"E:\\1.xlsx", "RU000A0JRKT8", "RU000A0JRKT8" }}; } @@ -45,7 +45,9 @@ static Object[][] getData() { void testIsin(String report, String firstIsin, String lastIsin) throws IOException { PsbBrokerReport psbBrokerReport = new PsbBrokerReport(report, securityRegistrar); List data = new DividendTable(psbBrokerReport).getData(); + //noinspection AssertBetweenInconvertibleTypes assertEquals(data.get(0).getSecurity(), firstIsin); + //noinspection AssertBetweenInconvertibleTypes assertEquals(data.get(data.size() - 1).getSecurity(), lastIsin); } } \ No newline at end of file diff --git a/src/test/java/ru/investbook/parser/psb/PortfolioCashTableTest.java b/src/test/java/ru/investbook/parser/psb/PortfolioCashTableTest.java index 8aa4a7bf..db67881b 100644 --- a/src/test/java/ru/investbook/parser/psb/PortfolioCashTableTest.java +++ b/src/test/java/ru/investbook/parser/psb/PortfolioCashTableTest.java @@ -35,8 +35,7 @@ public class PortfolioCashTableTest { @Mock SecurityRegistrar securityRegistrar; - @DataProvider(name = "cash_in") - Object[][] getData() { + static Object[][] cashIn() { return new Object[][] {{"E:\\1.xlsx", BigDecimal.valueOf(350.37)}}; } From bbd41291570b95feaa74d5e9a0a7b6da11759797 Mon Sep 17 00:00:00 2001 From: vananiev Date: Wed, 3 Jan 2024 23:58:50 +0300 Subject: [PATCH 06/17] replace Nullable annotation --- src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java | 2 +- src/main/java/ru/investbook/openformat/v1_1_0/AssetPof.java | 2 +- src/main/java/ru/investbook/openformat/v1_1_0/CashFlowPof.java | 2 +- src/main/java/ru/investbook/openformat/v1_1_0/PaymentPof.java | 2 +- .../investbook/openformat/v1_1_0/PortfolioOpenFormatV1_1_0.java | 2 +- src/main/java/ru/investbook/openformat/v1_1_0/TradePof.java | 2 +- src/main/java/ru/investbook/openformat/v1_1_0/TransferPof.java | 2 +- .../java/ru/investbook/parser/TransactionValueAndFeeParser.java | 2 +- .../ru/investbook/parser/uralsib/SecurityTransactionTable.java | 2 +- src/main/java/ru/investbook/report/DerivativeEvents.java | 2 +- .../specs/ForeignExchangeRateSearchSpecification.java | 2 +- .../ru/investbook/repository/specs/SpecificationHelper.java | 2 +- .../java/ru/investbook/web/forms/model/EventCashFlowModel.java | 2 +- .../java/ru/investbook/web/forms/model/PortfolioCashModel.java | 2 +- .../ru/investbook/web/forms/model/PortfolioPropertyModel.java | 2 +- .../ru/investbook/web/forms/model/SecurityDescriptionModel.java | 2 +- .../investbook/web/forms/model/SecurityEventCashFlowModel.java | 2 +- .../java/ru/investbook/web/forms/model/SecurityQuoteModel.java | 2 +- .../java/ru/investbook/web/forms/model/TransactionModel.java | 2 +- .../investbook/web/forms/service/SecurityRepositoryHelper.java | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java index 854f8a99..f7c70836 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java @@ -31,7 +31,7 @@ import org.spacious_team.broker.pojo.PortfolioProperty; import org.spacious_team.broker.pojo.PortfolioProperty.PortfolioPropertyBuilder; import org.spacious_team.broker.pojo.PortfolioPropertyType; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.entity.PortfolioEntity; import java.math.BigDecimal; diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/AssetPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/AssetPof.java index f96a18ae..e5aff07b 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/AssetPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/AssetPof.java @@ -28,7 +28,7 @@ import lombok.extern.slf4j.Slf4j; import org.spacious_team.broker.pojo.Security; import org.spacious_team.broker.pojo.SecurityType; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.entity.SecurityEntity; import java.util.Optional; diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/CashFlowPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/CashFlowPof.java index fc66cc3a..ce465e3e 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/CashFlowPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/CashFlowPof.java @@ -29,7 +29,7 @@ import lombok.extern.slf4j.Slf4j; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.EventCashFlow; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.entity.EventCashFlowEntity; import java.math.BigDecimal; diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/PaymentPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/PaymentPof.java index c0d2274e..e364c62c 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/PaymentPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/PaymentPof.java @@ -32,7 +32,7 @@ import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.SecurityEventCashFlow; import org.spacious_team.broker.pojo.SecurityType; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.entity.SecurityEventCashFlowEntity; import ru.investbook.openformat.OpenFormatHelper; diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/PortfolioOpenFormatV1_1_0.java b/src/main/java/ru/investbook/openformat/v1_1_0/PortfolioOpenFormatV1_1_0.java index 66b7c788..ae356eb2 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/PortfolioOpenFormatV1_1_0.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/PortfolioOpenFormatV1_1_0.java @@ -25,7 +25,7 @@ import lombok.Builder; import lombok.Value; import lombok.extern.jackson.Jacksonized; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collection; import java.util.Collections; diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/TradePof.java b/src/main/java/ru/investbook/openformat/v1_1_0/TradePof.java index 4d8f5074..45d2a741 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/TradePof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/TradePof.java @@ -34,7 +34,7 @@ import org.spacious_team.broker.report_parser.api.DerivativeTransaction; import org.spacious_team.broker.report_parser.api.ForeignExchangeTransaction; import org.spacious_team.broker.report_parser.api.SecurityTransaction; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.util.Assert; import ru.investbook.entity.SecurityEventCashFlowEntity; import ru.investbook.entity.TransactionCashFlowEntity; diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/TransferPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/TransferPof.java index 887989fd..c1247597 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/TransferPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/TransferPof.java @@ -30,7 +30,7 @@ import lombok.extern.slf4j.Slf4j; import org.spacious_team.broker.pojo.SecurityEventCashFlow; import org.spacious_team.broker.pojo.Transaction; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.entity.TransactionEntity; import java.math.BigDecimal; diff --git a/src/main/java/ru/investbook/parser/TransactionValueAndFeeParser.java b/src/main/java/ru/investbook/parser/TransactionValueAndFeeParser.java index f3bd6e86..3756005b 100644 --- a/src/main/java/ru/investbook/parser/TransactionValueAndFeeParser.java +++ b/src/main/java/ru/investbook/parser/TransactionValueAndFeeParser.java @@ -25,7 +25,7 @@ import lombok.extern.slf4j.Slf4j; import org.spacious_team.table_wrapper.api.TableHeaderColumn; import org.spacious_team.table_wrapper.api.TableRow; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.stereotype.Component; import ru.investbook.report.ForeignExchangeRateService; diff --git a/src/main/java/ru/investbook/parser/uralsib/SecurityTransactionTable.java b/src/main/java/ru/investbook/parser/uralsib/SecurityTransactionTable.java index 0016df6b..82d6c975 100644 --- a/src/main/java/ru/investbook/parser/uralsib/SecurityTransactionTable.java +++ b/src/main/java/ru/investbook/parser/uralsib/SecurityTransactionTable.java @@ -29,7 +29,7 @@ import org.spacious_team.table_wrapper.api.TableColumn; import org.spacious_team.table_wrapper.api.TableHeaderColumn; import org.spacious_team.table_wrapper.api.TableRow; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.parser.SingleAbstractReportTable; import ru.investbook.parser.TransactionValueAndFeeParser; diff --git a/src/main/java/ru/investbook/report/DerivativeEvents.java b/src/main/java/ru/investbook/report/DerivativeEvents.java index b42cab1f..3eca395d 100644 --- a/src/main/java/ru/investbook/report/DerivativeEvents.java +++ b/src/main/java/ru/investbook/report/DerivativeEvents.java @@ -24,7 +24,7 @@ import org.spacious_team.broker.pojo.SecurityEventCashFlow; import org.spacious_team.broker.pojo.Transaction; import org.spacious_team.broker.pojo.TransactionCashFlow; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import java.math.BigDecimal; import java.util.ArrayList; diff --git a/src/main/java/ru/investbook/repository/specs/ForeignExchangeRateSearchSpecification.java b/src/main/java/ru/investbook/repository/specs/ForeignExchangeRateSearchSpecification.java index c1afeb24..1fc59511 100644 --- a/src/main/java/ru/investbook/repository/specs/ForeignExchangeRateSearchSpecification.java +++ b/src/main/java/ru/investbook/repository/specs/ForeignExchangeRateSearchSpecification.java @@ -25,7 +25,7 @@ import jakarta.persistence.criteria.Root; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.domain.Specification; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.entity.ForeignExchangeRateEntity; import ru.investbook.entity.ForeignExchangeRateEntityPk_; import ru.investbook.entity.ForeignExchangeRateEntity_; diff --git a/src/main/java/ru/investbook/repository/specs/SpecificationHelper.java b/src/main/java/ru/investbook/repository/specs/SpecificationHelper.java index 7d7785c1..b83461d4 100644 --- a/src/main/java/ru/investbook/repository/specs/SpecificationHelper.java +++ b/src/main/java/ru/investbook/repository/specs/SpecificationHelper.java @@ -27,7 +27,7 @@ import jakarta.persistence.metamodel.SingularAttribute; import lombok.NoArgsConstructor; import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.entity.PortfolioEntity; import ru.investbook.entity.PortfolioEntity_; import ru.investbook.entity.SecurityEntity; diff --git a/src/main/java/ru/investbook/web/forms/model/EventCashFlowModel.java b/src/main/java/ru/investbook/web/forms/model/EventCashFlowModel.java index 65c4bafc..d7a2fb90 100644 --- a/src/main/java/ru/investbook/web/forms/model/EventCashFlowModel.java +++ b/src/main/java/ru/investbook/web/forms/model/EventCashFlowModel.java @@ -22,9 +22,9 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import lombok.Data; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.lang.Nullable; import java.math.BigDecimal; import java.time.LocalDate; diff --git a/src/main/java/ru/investbook/web/forms/model/PortfolioCashModel.java b/src/main/java/ru/investbook/web/forms/model/PortfolioCashModel.java index 672053a1..3ec7c7b8 100644 --- a/src/main/java/ru/investbook/web/forms/model/PortfolioCashModel.java +++ b/src/main/java/ru/investbook/web/forms/model/PortfolioCashModel.java @@ -23,7 +23,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import java.math.BigDecimal; import java.time.LocalDate; diff --git a/src/main/java/ru/investbook/web/forms/model/PortfolioPropertyModel.java b/src/main/java/ru/investbook/web/forms/model/PortfolioPropertyModel.java index 5bdea71d..5199a452 100644 --- a/src/main/java/ru/investbook/web/forms/model/PortfolioPropertyModel.java +++ b/src/main/java/ru/investbook/web/forms/model/PortfolioPropertyModel.java @@ -22,7 +22,7 @@ import jakarta.validation.constraints.NotNull; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import java.time.LocalDate; import java.time.LocalTime; diff --git a/src/main/java/ru/investbook/web/forms/model/SecurityDescriptionModel.java b/src/main/java/ru/investbook/web/forms/model/SecurityDescriptionModel.java index fa7a8be9..10c0837a 100644 --- a/src/main/java/ru/investbook/web/forms/model/SecurityDescriptionModel.java +++ b/src/main/java/ru/investbook/web/forms/model/SecurityDescriptionModel.java @@ -21,7 +21,7 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; @Data public class SecurityDescriptionModel { diff --git a/src/main/java/ru/investbook/web/forms/model/SecurityEventCashFlowModel.java b/src/main/java/ru/investbook/web/forms/model/SecurityEventCashFlowModel.java index 205c3375..4a25ae6b 100644 --- a/src/main/java/ru/investbook/web/forms/model/SecurityEventCashFlowModel.java +++ b/src/main/java/ru/investbook/web/forms/model/SecurityEventCashFlowModel.java @@ -24,7 +24,7 @@ import lombok.Data; import org.spacious_team.broker.pojo.CashFlowType; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import java.math.BigDecimal; import java.time.LocalDate; diff --git a/src/main/java/ru/investbook/web/forms/model/SecurityQuoteModel.java b/src/main/java/ru/investbook/web/forms/model/SecurityQuoteModel.java index 6ed066b9..5e37b718 100644 --- a/src/main/java/ru/investbook/web/forms/model/SecurityQuoteModel.java +++ b/src/main/java/ru/investbook/web/forms/model/SecurityQuoteModel.java @@ -23,7 +23,7 @@ import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.PositiveOrZero; import lombok.Data; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import java.math.BigDecimal; import java.time.Instant; diff --git a/src/main/java/ru/investbook/web/forms/model/TransactionModel.java b/src/main/java/ru/investbook/web/forms/model/TransactionModel.java index 4741b9ec..7543cedc 100644 --- a/src/main/java/ru/investbook/web/forms/model/TransactionModel.java +++ b/src/main/java/ru/investbook/web/forms/model/TransactionModel.java @@ -25,7 +25,7 @@ import jakarta.xml.bind.DatatypeConverter; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.util.StringUtils; import java.math.BigDecimal; diff --git a/src/main/java/ru/investbook/web/forms/service/SecurityRepositoryHelper.java b/src/main/java/ru/investbook/web/forms/service/SecurityRepositoryHelper.java index 87c430d8..780589de 100644 --- a/src/main/java/ru/investbook/web/forms/service/SecurityRepositoryHelper.java +++ b/src/main/java/ru/investbook/web/forms/service/SecurityRepositoryHelper.java @@ -19,7 +19,7 @@ package ru.investbook.web.forms.service; import lombok.RequiredArgsConstructor; -import org.springframework.lang.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.springframework.util.StringUtils; From 631cdb815ad3de0c0103cb546fb058ef058e8a87 Mon Sep 17 00:00:00 2001 From: vananiev Date: Thu, 4 Jan 2024 01:45:01 +0300 Subject: [PATCH 07/17] fix controller npe warns --- .../v1_1_0/PortfolioOpenFormatV1_1_0.java | 29 ++++++------------- .../report/ForeignExchangeRateService.java | 20 ++++++++----- .../moex/MoexDerivativeCodeService.java | 23 ++++++++------- 3 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/PortfolioOpenFormatV1_1_0.java b/src/main/java/ru/investbook/openformat/v1_1_0/PortfolioOpenFormatV1_1_0.java index ae356eb2..9443b594 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/PortfolioOpenFormatV1_1_0.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/PortfolioOpenFormatV1_1_0.java @@ -21,14 +21,14 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.Value; import lombok.extern.jackson.Jacksonized; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collection; -import java.util.Collections; + +import static java.util.Collections.emptySet; @Jacksonized @Builder @@ -38,22 +38,18 @@ public class PortfolioOpenFormatV1_1_0 { public static String GENERATED_BY_INVESTBOOK = "investbook"; - @NotNull @Builder.Default @JsonProperty("version") String version = "1.1.0"; - @NotNull @Builder.Default @JsonProperty("generated-by") String generatedBy = GENERATED_BY_INVESTBOOK; - @NotNull @Builder.Default @JsonProperty("generated") long generated = System.currentTimeMillis() / 1000; - @NotNull @JsonProperty("end") long end; @@ -61,40 +57,33 @@ public class PortfolioOpenFormatV1_1_0 { @JsonProperty("start") Long start; - @NotNull @Builder.Default @JsonProperty("accounts") - Collection accounts = Collections.emptySet(); + Collection accounts = emptySet(); - @NotNull @Builder.Default @JsonProperty("cash-balances") - Collection cashBalances = Collections.emptySet(); + Collection cashBalances = emptySet(); - @NotNull @Builder.Default @JsonProperty("assets") - Collection assets = Collections.emptySet(); + Collection assets = emptySet(); - @NotNull @Builder.Default @JsonProperty("trades") - Collection trades = Collections.emptySet(); + Collection trades = emptySet(); - @NotNull @Builder.Default @JsonProperty("transfers") - Collection transfer = Collections.emptySet(); + Collection transfer = emptySet(); - @NotNull @Builder.Default @JsonProperty("payments") - Collection payments = Collections.emptySet(); + Collection payments = emptySet(); - @NotNull @Builder.Default @JsonProperty("cash-flows") - Collection cashFlows = Collections.emptySet(); + Collection cashFlows = emptySet(); @Nullable @JsonProperty("vnd-investbook") diff --git a/src/main/java/ru/investbook/report/ForeignExchangeRateService.java b/src/main/java/ru/investbook/report/ForeignExchangeRateService.java index 652e5e38..8f857250 100644 --- a/src/main/java/ru/investbook/report/ForeignExchangeRateService.java +++ b/src/main/java/ru/investbook/report/ForeignExchangeRateService.java @@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.SecurityQuote; import org.spacious_team.broker.pojo.SecurityQuote.SecurityQuoteBuilder; import org.spacious_team.broker.pojo.SecurityType; @@ -56,6 +57,7 @@ public class ForeignExchangeRateService { // base-currency -> quote-currency -> local date -> exchange-rate private final Map>> cacheByDate = new ConcurrentHashMap<>(); @Value("${server.port}") + @SuppressWarnings("unused") private int serverPort; /** @@ -69,7 +71,7 @@ public BigDecimal getExchangeRate(String baseCurrency, String quoteCurrency) { if (baseCurrency.equalsIgnoreCase(quoteCurrency)) { return BigDecimal.ONE; } - BigDecimal exchangeRate = getFromCache(baseCurrency, quoteCurrency); + @Nullable BigDecimal exchangeRate = getFromCache(baseCurrency, quoteCurrency); if (exchangeRate != null) { return exchangeRate; } else if (baseCurrency.equalsIgnoreCase("RUB")) { @@ -141,7 +143,7 @@ public BigDecimal getExchangeRate(String baseCurrency, String quoteCurrency, Loc if (baseCurrency.equalsIgnoreCase(quoteCurrency)) { return BigDecimal.ONE; } - BigDecimal exchangeRate = getFromCache(baseCurrency, quoteCurrency, atDate); + @Nullable BigDecimal exchangeRate = getFromCache(baseCurrency, quoteCurrency, atDate); if (exchangeRate != null) { return exchangeRate; } else if (baseCurrency.equalsIgnoreCase("RUB")) { @@ -216,12 +218,12 @@ private void cache(String baseCurrency, String quoteCurrency, LocalDate localDat .putIfAbsent(localDate, exchangeRate); } - private BigDecimal getFromCache(String baseCurrency, String quoteCurrency) { + private @Nullable BigDecimal getFromCache(String baseCurrency, String quoteCurrency) { return this.cache.computeIfAbsent(baseCurrency, k -> new ConcurrentHashMap<>()) .get(quoteCurrency); } - private BigDecimal getFromCache(String baseCurrency, String quoteCurrency, LocalDate localDate) { + private @Nullable BigDecimal getFromCache(String baseCurrency, String quoteCurrency, LocalDate localDate) { return this.cacheByDate.computeIfAbsent(baseCurrency, k -> new ConcurrentHashMap<>()) .computeIfAbsent(quoteCurrency, k -> new ConcurrentHashMap<>()) .get(localDate); @@ -263,7 +265,7 @@ public BigDecimal convertValueToCurrency(BigDecimal value, String fromCurrency, * курсу по-умолчанию, если официальный курс не известен. */ public SecurityQuote convertQuoteToCurrency(SecurityQuote quote, String toCurrency, SecurityType securityType) { - String fromCurrency = quote.getCurrency(); + @Nullable String fromCurrency = quote.getCurrency(); if (fromCurrency == null || fromCurrency.equalsIgnoreCase(toCurrency)) { return quote; } else { @@ -277,13 +279,15 @@ private static SecurityQuoteBuilder convertQuoteToCurrency(SecurityQuote quote, Supplier exchangeRateSupplier, SecurityType securityType) { BigDecimal exchangeRate = exchangeRateSupplier.get(); - BigDecimal accruedInterest = quote.getAccruedInterest(); + BigDecimal quoteValue = quote.getQuote(); + @Nullable BigDecimal price = quote.getPrice(); + @Nullable BigDecimal accruedInterest = quote.getAccruedInterest(); boolean nonCurrencyQuote = (securityType == BOND) || ((securityType == STOCK_OR_BOND) && (accruedInterest != null)) || (securityType == DERIVATIVE); return quote.toBuilder() - .quote(nonCurrencyQuote ? quote.getQuote() : quote.getQuote().multiply(exchangeRate)) - .price((quote.getPrice() == null) ? null : quote.getPrice().multiply(exchangeRate)) + .quote(nonCurrencyQuote ? quoteValue : quoteValue.multiply(exchangeRate)) + .price((price == null) ? null : price.multiply(exchangeRate)) .accruedInterest((accruedInterest == null) ? null : accruedInterest.multiply(exchangeRate)); } } diff --git a/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java b/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java index 4fc47ea8..8d3441f8 100644 --- a/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java +++ b/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java @@ -19,6 +19,7 @@ package ru.investbook.service.moex; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.stereotype.Component; import java.time.LocalDate; @@ -37,7 +38,7 @@ /** * Converts derivative short names (for ex. Si-6.21) to secid (a.k.a ticker codes, for ex SiM1) * - * @See Опционы на акции + * @see Опционы на акции * @see Specifications ticker codes for Futures and Options */ @Slf4j @@ -221,7 +222,7 @@ public boolean isFutures(String contract) { /** * @return true for futures in format {@code SiM1} */ - public boolean isFuturesCode(String contract) { + public boolean isFuturesCode(@Nullable String contract) { return contract != null && contract.length() == 4 && hasOnlyLetters(contract, 0, 2) && // smells like a futures code @@ -232,7 +233,7 @@ public boolean isFuturesCode(String contract) { /** * @return true for futures in format {@code Si-3.21} */ - public boolean isFuturesShortname(String contract) { + public boolean isFuturesShortname(@Nullable String contract) { try { if (contract == null) return false; int dotIdx = contract.length() - 3; @@ -254,7 +255,7 @@ public boolean isFuturesShortname(String contract) { return false; } - private boolean hasOnlyLetters(String string, int beginIndex, int endIndex) { + private boolean hasOnlyLetters(String string, @SuppressWarnings("SameParameterValue") int beginIndex, int endIndex) { int i = Math.max(0, beginIndex); i = Math.min(string.length(), i); int cnt = Math.max(0, endIndex); @@ -279,7 +280,7 @@ public boolean isOption(String contract) { /** * @return true for option in format {@code BR10BF0}, {@code BR-10BF0} (знак "-" это часть цены) and {@code GZ300CG2D} */ - public boolean isOptionCode(String contract) { + public boolean isOptionCode(@Nullable String contract) { if (contract == null) return false; int length = contract.length(); if (length > 5) { @@ -300,7 +301,7 @@ public boolean isOptionCode(String contract) { * @return true for option in format {@code BR-7.20M250620СA10}, {@code BR-7.20M250620СA-10}, * {@code BR-7.16M270616CA 50} and {@code GAZPP220722CE 300} */ - public boolean isOptionShortname(String contract) { + public boolean isOptionShortname(@Nullable String contract) { if (contract == null) return false; int EIdx = getOptionExpirationTypeCharPosition(contract); if (EIdx == -1) { @@ -367,7 +368,7 @@ private int getOptionAccountTypeCharPosition(String contract, int expirationChar /** * @return {@code SiM1} for futures contract in {@code Si-6.21} or {@code SiM1} format */ - public Optional getFuturesCode(String contract) { + public Optional getFuturesCode(@Nullable String contract) { try { if (contract == null) return empty(); int dashIdx = contract.indexOf('-'); @@ -390,8 +391,10 @@ public Optional getFuturesCode(String contract) { /** * @return {@code Si-6.21} for futures contract in {@code Si-6.21} or {@code SiM1} format */ - public Optional getFuturesShortname(String contract) { - if (isFuturesShortname(contract)) { + public Optional getFuturesShortname(@Nullable String contract) { + if (contract == null) { + return Optional.empty(); + } else if (isFuturesShortname(contract)) { return Optional.of(contract); } else if (contract.length() != 4) { return empty(); @@ -415,7 +418,7 @@ public Optional getFuturesShortname(String contract) { /** * Convert derivative codes before storing to DB if you need */ - public String convertDerivativeCode(String code) { + public @Nullable String convertDerivativeCode(@Nullable String code) { return isFuturesCode(code) ? getFuturesShortname(code).orElse(code) : code; From fe97f25e20f5ae78bf3be56225e2f43a6f7642f9 Mon Sep 17 00:00:00 2001 From: vananiev Date: Thu, 4 Jan 2024 01:55:52 +0300 Subject: [PATCH 08/17] fix unit tests workflow --- .github/workflows/unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 82042fba..1b439176 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -22,7 +22,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: '20' + java-version: '21' distribution: 'liberica' cache: maven From f1b06f8a40fa123c511ee1bdc31af1688bdd19d3 Mon Sep 17 00:00:00 2001 From: vananiev Date: Fri, 5 Jan 2024 01:49:40 +0300 Subject: [PATCH 09/17] fix npe warns for TradePof --- .../openformat/OpenFormatHelper.java | 9 +-- .../openformat/v1_1_0/TradePof.java | 60 ++++++++++--------- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/src/main/java/ru/investbook/openformat/OpenFormatHelper.java b/src/main/java/ru/investbook/openformat/OpenFormatHelper.java index 8d864d98..0a616c2b 100644 --- a/src/main/java/ru/investbook/openformat/OpenFormatHelper.java +++ b/src/main/java/ru/investbook/openformat/OpenFormatHelper.java @@ -18,22 +18,23 @@ package ru.investbook.openformat; -import org.springframework.util.StringUtils; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Objects; +import static org.springframework.util.StringUtils.hasLength; import static ru.investbook.entity.SecurityEntity.isinPattern; public class OpenFormatHelper { - public static String getValidCurrencyOrNull(String currency) { + public static @Nullable String getValidCurrencyOrNull(@Nullable String currency) { if (currency == null) return null; currency = currency.toUpperCase(); return Objects.equals(currency, "RUR") ? "RUB" : currency; } - public static String getValidIsinOrNull(String isin) { - return StringUtils.hasLength(isin) && isinPattern.matcher(isin).matches() ? + public static @Nullable String getValidIsinOrNull(@Nullable String isin) { + return hasLength(isin) && isinPattern.matcher(isin).matches() ? isin : null; } diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/TradePof.java b/src/main/java/ru/investbook/openformat/v1_1_0/TradePof.java index 45d2a741..c8a80532 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/TradePof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/TradePof.java @@ -22,19 +22,18 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.Value; import lombok.extern.jackson.Jacksonized; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.SecurityType; import org.spacious_team.broker.report_parser.api.AbstractTransaction; import org.spacious_team.broker.report_parser.api.DerivativeTransaction; import org.spacious_team.broker.report_parser.api.ForeignExchangeTransaction; import org.spacious_team.broker.report_parser.api.SecurityTransaction; -import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.util.Assert; import ru.investbook.entity.SecurityEventCashFlowEntity; import ru.investbook.entity.TransactionCashFlowEntity; @@ -60,33 +59,32 @@ @Slf4j public class TradePof { - @NotNull @JsonProperty("id") int id; - @NotEmpty @JsonProperty("trade-id") + @NotEmpty String tradeId; /** + * Дата и время сделки. * Если значение null, то значение поля settlement обязательно */ - @Nullable @JsonProperty("timestamp") + @Nullable Long timestamp; /** + * Дата и время поставки. * Если значение null, то значение поля timestamp обязательно */ - @Nullable @JsonProperty("settlement") + @Nullable Long settlement; - @NotNull @JsonProperty("account") int account; - @NotNull @Getter(AccessLevel.NONE) @JsonProperty("asset") int asset; @@ -95,45 +93,48 @@ public class TradePof { * Если '+', то это покупка, если '-', то это продажа. * Поддерживаются дробные акции */ - @NotNull @JsonProperty("count") BigDecimal count; /** - * В валюте сделки, для облигации - без НКД, для деривативов в валюте - опционально + * Цена бумаги/контракта (за единицу) в валюте сделки, + * для облигации - без учета НКД, для деривативов поле может отсутствовать */ - @Nullable @JsonProperty("price") + @Nullable BigDecimal price; - @Nullable @JsonProperty("accrued-interest") + @Nullable BigDecimal accruedInterest; /** - * Котировка. Для облигации в процентах, для деривативов в пунктах + * Котировка. Для облигации в процентах, для деривативов в пунктах. + * Для деривативов - обязательное поле, для облигаций - опциональное. */ - @Nullable @JsonProperty("quote") + @Nullable BigDecimal quote; - @Nullable + /** + * Может отсутствовать, если поле 'price' и 'accrued-interest' отсутствуют + */ @JsonProperty("currency") + @Nullable String currency; /** * Комиссия. Если отрицательное значение, значит возврат комиссии */ - @NotNull @JsonProperty("fee") BigDecimal fee; - @NotEmpty @JsonProperty("fee-currency") + @NotEmpty String feeCurrency; - @Nullable @JsonProperty("description") + @Nullable String description; static TradePof of(TransactionEntity transaction, @@ -165,8 +166,8 @@ static TradePof of(TransactionEntity transaction, .filter(e -> e.getCashFlowType().getId() == FEE.getId()) .findAny() .ifPresentOrElse(e -> builder - .fee(e.getValue().negate()) - .feeCurrency(getValidCurrencyOrNull(e.getCurrency())), + .fee(e.getValue().negate()) + .feeCurrency(requireNonNull(getValidCurrencyOrNull(e.getCurrency()))), () -> builder.fee(BigDecimal.ZERO).feeCurrency("RUB")); return builder.build(); } @@ -204,18 +205,19 @@ Optional toTransaction(Map accountToPortfo Map assetTypes) { try { SecurityType securityType = requireNonNull(assetTypes.get(asset)); + @Nullable BigDecimal value = (price == null) ? null : price.multiply(count).negate(); AbstractTransaction.AbstractTransactionBuilder builder = switch (securityType) { case STOCK, BOND, STOCK_OR_BOND, ASSET -> SecurityTransaction.builder() - .value(requireNonNull(price).multiply(count).negate()) + .value(requireNonNull(value)) .accruedInterest((accruedInterest == null) ? null : accruedInterest.multiply(count).negate()) - .valueCurrency(getValidCurrencyOrNull(requireNonNull(currency))); + .valueCurrency(requireNonNull(getValidCurrencyOrNull(currency))); case DERIVATIVE -> DerivativeTransaction.builder() - .valueInPoints(requireNonNull(quote).multiply(count).negate()) - .value((price == null) ? null : price.multiply(count).negate()) // для деривативов - опциональное - .valueCurrency(getValidCurrencyOrNull(currency)); // для деривативов - опциональное + .valueInPoints(requireNonNull(quote).multiply(count).negate()) + .value(value) // для деривативов - опциональное + .valueCurrency(getValidCurrencyOrNull(currency)); // для деривативов - опциональное case CURRENCY_PAIR -> ForeignExchangeTransaction.builder() - .value(requireNonNull(price).multiply(count).negate()) - .valueCurrency(getValidCurrencyOrNull(requireNonNull(currency))); + .value(requireNonNull(value)) + .valueCurrency(requireNonNull(getValidCurrencyOrNull(currency))); }; long ts = requireNonNull(getSettlementOrTimestamp()); @@ -225,8 +227,8 @@ Optional toTransaction(Map accountToPortfo .security(getSecurityId(assetToSecurityId)) .count(count.intValueExact()) .timestamp(Instant.ofEpochSecond(ts)) - .fee((fee == null) ? null : fee.negate()) - .feeCurrency(getValidCurrencyOrNull(feeCurrency)) + .fee(fee.negate()) + .feeCurrency(requireNonNull(getValidCurrencyOrNull(feeCurrency))) .build()); } catch (Exception e) { log.error("Не могу распарсить {}", this, e); From f9dd29966e3abf60ed9d40f677e7cd99559abe6f Mon Sep 17 00:00:00 2001 From: vananiev Date: Sun, 7 Jan 2024 00:08:18 +0300 Subject: [PATCH 10/17] fix nullable warns --- .../SecurityDescriptionConverter.java | 3 +- .../converter/TransactionConverter.java | 1 + .../SecurityDescriptionRepository.java | 7 +- .../moex/MoexDerivativeCodeService.java | 2 +- .../forms/model/ArchivedPortfolioModel.java | 2 - .../web/forms/model/EventCashFlowModel.java | 50 +++++------ .../forms/model/ForeignExchangeRateModel.java | 12 +-- .../web/forms/model/PageableWrapperModel.java | 9 +- .../web/forms/model/PortfolioCashModel.java | 16 +--- .../forms/model/PortfolioPropertyModel.java | 11 +-- .../PortfolioPropertyTotalAssetsModel.java | 3 - .../forms/model/SecurityDescriptionModel.java | 15 ++-- .../model/SecurityEventCashFlowModel.java | 36 +++----- .../web/forms/model/SecurityHelper.java | 7 +- .../web/forms/model/SecurityQuoteModel.java | 25 ++---- .../web/forms/model/SplitModel.java | 20 ++--- .../web/forms/model/TransactionModel.java | 85 +++++++------------ .../service/EventCashFlowFormsService.java | 4 +- .../PortfolioPropertyFormsService.java | 5 +- .../SecurityEventCashFlowFormsService.java | 2 +- .../service/SecurityQuoteFormsService.java | 6 +- .../service/SecurityRepositoryHelper.java | 28 +++--- .../service/TransactionFormsService.java | 5 +- 23 files changed, 143 insertions(+), 211 deletions(-) diff --git a/src/main/java/ru/investbook/converter/SecurityDescriptionConverter.java b/src/main/java/ru/investbook/converter/SecurityDescriptionConverter.java index e660e6e8..70f6f7bc 100644 --- a/src/main/java/ru/investbook/converter/SecurityDescriptionConverter.java +++ b/src/main/java/ru/investbook/converter/SecurityDescriptionConverter.java @@ -19,6 +19,7 @@ package ru.investbook.converter; import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.SecurityDescription; import org.springframework.stereotype.Component; import ru.investbook.entity.IssuerEntity; @@ -33,7 +34,7 @@ public class SecurityDescriptionConverter implements EntityConverter { @Transactional - default void createOrUpdateSector(int securityId, String sector) { + default void createOrUpdateSector(int securityId, @Nullable String sector) { if (existsById(securityId)) { updateSector(securityId, sector); } else { @@ -41,10 +42,10 @@ default void createOrUpdateSector(int securityId, String sector) { @Transactional @Modifying @Query("UPDATE SecurityDescriptionEntity SET sector = :sector WHERE security = :securityId") - void updateSector(int securityId, String sector); + void updateSector(int securityId, @Nullable String sector); @Transactional - default void createSectorIfNotExists(int securityId, String sector) { + default void createSectorIfNotExists(int securityId, @Nullable String sector) { if (!existsById(securityId)) { SecurityDescriptionEntity entity = new SecurityDescriptionEntity(); entity.setSecurity(securityId); diff --git a/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java b/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java index 8d3441f8..da729c45 100644 --- a/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java +++ b/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java @@ -403,7 +403,7 @@ public Optional getFuturesShortname(@Nullable String contract) { if (month != -1) { char yearChar = contract.charAt(3); if (isDigit(yearChar)) { - String prefix = codeToShortnames.get(contract.substring(0, 2)); + @Nullable String prefix = codeToShortnames.get(contract.substring(0, 2)); if (prefix != null) { int year = getShortnameYear(yearChar); if (year != -1) { diff --git a/src/main/java/ru/investbook/web/forms/model/ArchivedPortfolioModel.java b/src/main/java/ru/investbook/web/forms/model/ArchivedPortfolioModel.java index b124d8cc..ab3cadbc 100644 --- a/src/main/java/ru/investbook/web/forms/model/ArchivedPortfolioModel.java +++ b/src/main/java/ru/investbook/web/forms/model/ArchivedPortfolioModel.java @@ -18,7 +18,6 @@ package ru.investbook.web.forms.model; -import jakarta.validation.constraints.NotNull; import lombok.Data; import java.util.Set; @@ -26,7 +25,6 @@ @Data public class ArchivedPortfolioModel { - @NotNull private Set portfolios; } diff --git a/src/main/java/ru/investbook/web/forms/model/EventCashFlowModel.java b/src/main/java/ru/investbook/web/forms/model/EventCashFlowModel.java index d7a2fb90..614a5824 100644 --- a/src/main/java/ru/investbook/web/forms/model/EventCashFlowModel.java +++ b/src/main/java/ru/investbook/web/forms/model/EventCashFlowModel.java @@ -19,7 +19,6 @@ package ru.investbook.web.forms.model; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import lombok.Data; import org.checkerframework.checker.nullness.qual.Nullable; @@ -37,47 +36,41 @@ @Data public class EventCashFlowModel { - @Nullable - private Integer id; + private @Nullable Integer id; - @NotEmpty - private String portfolio; + private @NotEmpty String portfolio; - @NotNull @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate date = LocalDate.now(); - @NotNull @DateTimeFormat(pattern = "HH:mm:ss") private LocalTime time = LocalTime.NOON; - @NotNull private CashFlowType type; /** * Negative value for cash out, otherwise - positive */ - @NotNull private BigDecimal value; - @NotEmpty - private String valueCurrency = "RUB"; + private @NotEmpty String valueCurrency = "RUB"; - @Nullable - private String description; + private @Nullable String description; /** * Используется для привязки выплаты к бумаге из того же или другого счета */ - @Nullable - private AttachedSecurity attachedSecurity = new AttachedSecurity(); + private @Nullable AttachedSecurity attachedSecurity = new AttachedSecurity(); public void setValueCurrency(String currency) { this.valueCurrency = currency.toUpperCase(); } + /** + * Used by templates/events/edit-form.html + */ + @SuppressWarnings("unused") public String getStringType() { - if (type == null) return null; return switch (type) { case DIVIDEND, COUPON, REDEMPTION, AMORTIZATION, ACCRUED_INTEREST -> type.name(); case CASH -> { @@ -87,13 +80,14 @@ public String getStringType() { } yield type.name() + (isValuePositive() ? "_IN" : "_OUT"); } - default -> type.name() + (isValuePositive() ? "_IN" : "_OUT"); + default -> type.name() + (isValuePositive() ? "_IN" : "_OUT"); }; } /** * Used by templates/events/edit-form.html */ + @SuppressWarnings("unused") public void setStringType(String value) { if (value.equals("TAX_IIS_A")) { type = CashFlowType.CASH; @@ -113,18 +107,14 @@ public boolean isAttachedToSecurity() { @Data public class AttachedSecurity { - @Nullable - private Integer securityEventCashFlowId; + private @Nullable Integer securityEventCashFlowId; /** * In "name (isin)" or "contract-name" format */ - @Nullable - private String security; + private @Nullable String security; - @Nullable - @Positive - private Integer count; + private @Nullable @Positive Integer count; public boolean isValid() { return hasLength(security) && @@ -135,8 +125,10 @@ public boolean isValid() { /** * Returns ISIN if description in "Name (ISIN)" format, null otherwise */ - public String getSecurityIsin() { - return SecurityHelper.getSecurityIsin(requireNonNull(security)); + public @Nullable String getSecurityIsin() { + @SuppressWarnings("nullness") + String securityDescription = requireNonNull(security); + return SecurityHelper.getSecurityIsin(securityDescription); } /** @@ -145,14 +137,16 @@ public String getSecurityIsin() { public String getSecurityName() { // Для Типа выплаты TAX может выдавать неверный тип бумаги, // но для текущего алгоритма SecurityHelper.getSecurityName() типа достаточно - SecurityType securityType = switch(type) { + SecurityType securityType = switch (type) { case DIVIDEND -> SecurityType.SHARE; case ACCRUED_INTEREST, AMORTIZATION, REDEMPTION, COUPON -> SecurityType.BOND; case DERIVATIVE_PROFIT, DERIVATIVE_PRICE, DERIVATIVE_QUOTE -> SecurityType.DERIVATIVE; case TAX -> SecurityType.SHARE; // для TAX выдает не верный тип бумаги default -> throw new IllegalArgumentException("Не смог получить тип ЦБ по типу выплаты: " + type); }; - return SecurityHelper.getSecurityName(security, securityType); + @SuppressWarnings("nullness") + String securityDescription = requireNonNull(security); + return SecurityHelper.getSecurityName(securityDescription, securityType); } } } diff --git a/src/main/java/ru/investbook/web/forms/model/ForeignExchangeRateModel.java b/src/main/java/ru/investbook/web/forms/model/ForeignExchangeRateModel.java index 8f0d307f..4e4ac444 100644 --- a/src/main/java/ru/investbook/web/forms/model/ForeignExchangeRateModel.java +++ b/src/main/java/ru/investbook/web/forms/model/ForeignExchangeRateModel.java @@ -19,7 +19,6 @@ package ru.investbook.web.forms.model; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; @@ -30,19 +29,14 @@ @Data public class ForeignExchangeRateModel { - @NotNull @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate date = LocalDate.now(); - @NotEmpty - private String baseCurrency; + private @NotEmpty String baseCurrency; - @NotEmpty - String quoteCurrency; + @NotEmpty String quoteCurrency; - @NotNull - @Positive - private BigDecimal rate; + private @Positive BigDecimal rate; public void setBaseCurrency(String currency) { this.baseCurrency = currency.toUpperCase(); diff --git a/src/main/java/ru/investbook/web/forms/model/PageableWrapperModel.java b/src/main/java/ru/investbook/web/forms/model/PageableWrapperModel.java index 97383f54..259abc4c 100644 --- a/src/main/java/ru/investbook/web/forms/model/PageableWrapperModel.java +++ b/src/main/java/ru/investbook/web/forms/model/PageableWrapperModel.java @@ -19,6 +19,7 @@ package ru.investbook.web.forms.model; import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.data.domain.Page; import java.util.List; @@ -35,18 +36,22 @@ public int getTotal() { return page.getTotalPages(); } + @SuppressWarnings("unused") public int getCurrent() { return page.getNumber(); } - public Integer getNext() { + @SuppressWarnings("unused") + public @Nullable Integer getNext() { return page.hasNext() ? page.nextPageable().getPageNumber() : null; } - public Integer getPrevious() { + @SuppressWarnings("unused") + public @Nullable Integer getPrevious() { return page.hasPrevious() ? page.previousPageable().getPageNumber(): null; } + @SuppressWarnings("unused") public boolean isLast() { return page.isLast(); } diff --git a/src/main/java/ru/investbook/web/forms/model/PortfolioCashModel.java b/src/main/java/ru/investbook/web/forms/model/PortfolioCashModel.java index 3ec7c7b8..967e59bc 100644 --- a/src/main/java/ru/investbook/web/forms/model/PortfolioCashModel.java +++ b/src/main/java/ru/investbook/web/forms/model/PortfolioCashModel.java @@ -19,11 +19,10 @@ package ru.investbook.web.forms.model; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; -import org.springframework.format.annotation.DateTimeFormat; import org.checkerframework.checker.nullness.qual.Nullable; +import org.springframework.format.annotation.DateTimeFormat; import java.math.BigDecimal; import java.time.LocalDate; @@ -33,26 +32,19 @@ @EqualsAndHashCode public class PortfolioCashModel { - @Nullable - private Integer id; + private @Nullable Integer id; - @NotEmpty - private String portfolio; + private @NotEmpty String portfolio; - @Nullable - private String market; + private @Nullable String market; - @NotNull @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate date = LocalDate.now(); - @NotNull @DateTimeFormat(pattern = "HH:mm:ss") private LocalTime time = LocalTime.NOON; - @NotNull private BigDecimal cash = BigDecimal.ZERO; - @NotNull private String currency = "RUB"; } diff --git a/src/main/java/ru/investbook/web/forms/model/PortfolioPropertyModel.java b/src/main/java/ru/investbook/web/forms/model/PortfolioPropertyModel.java index 5199a452..621ff2fc 100644 --- a/src/main/java/ru/investbook/web/forms/model/PortfolioPropertyModel.java +++ b/src/main/java/ru/investbook/web/forms/model/PortfolioPropertyModel.java @@ -19,10 +19,9 @@ package ru.investbook.web.forms.model; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; import org.checkerframework.checker.nullness.qual.Nullable; +import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDate; import java.time.LocalTime; @@ -30,17 +29,13 @@ @Data public abstract class PortfolioPropertyModel { - @Nullable - private Integer id; + private @Nullable Integer id; - @NotEmpty - private String portfolio; + private @NotEmpty String portfolio; - @NotNull @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate date = LocalDate.now(); - @NotNull @DateTimeFormat(pattern = "HH:mm:ss") private LocalTime time = LocalTime.NOON; } diff --git a/src/main/java/ru/investbook/web/forms/model/PortfolioPropertyTotalAssetsModel.java b/src/main/java/ru/investbook/web/forms/model/PortfolioPropertyTotalAssetsModel.java index 32c12171..e05e2e87 100644 --- a/src/main/java/ru/investbook/web/forms/model/PortfolioPropertyTotalAssetsModel.java +++ b/src/main/java/ru/investbook/web/forms/model/PortfolioPropertyTotalAssetsModel.java @@ -18,7 +18,6 @@ package ru.investbook.web.forms.model; -import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; import org.spacious_team.broker.pojo.PortfolioPropertyType; @@ -32,10 +31,8 @@ @EqualsAndHashCode(callSuper = true) public class PortfolioPropertyTotalAssetsModel extends PortfolioPropertyModel { - @NotNull private BigDecimal totalAssets; - @NotNull private Currency totalAssetsCurrency; public enum Currency { diff --git a/src/main/java/ru/investbook/web/forms/model/SecurityDescriptionModel.java b/src/main/java/ru/investbook/web/forms/model/SecurityDescriptionModel.java index 10c0837a..7650c2e4 100644 --- a/src/main/java/ru/investbook/web/forms/model/SecurityDescriptionModel.java +++ b/src/main/java/ru/investbook/web/forms/model/SecurityDescriptionModel.java @@ -19,28 +19,23 @@ package ru.investbook.web.forms.model; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import lombok.Data; import org.checkerframework.checker.nullness.qual.Nullable; @Data public class SecurityDescriptionModel { - @Nullable - private Integer securityId; + private @Nullable Integer securityId; /** * In "name (isin)" or "contract-name" format */ - @NotEmpty - private String security = "Наименование (RU0000000000)"; + private @NotEmpty String security = "Наименование (RU0000000000)"; - @NotEmpty - private String sector; + private @Nullable @NotEmpty String sector; - @NotNull private SecurityType securityType; - public void setSecurity(Integer securityId, String securityIsin, String securityName, SecurityType securityType) { + public void setSecurity(Integer securityId, @Nullable String securityIsin, @Nullable String securityName, SecurityType securityType) { this.securityId = securityId; this.security = SecurityHelper.getSecurityDescription(securityIsin, securityName, securityType); this.securityType = securityType; @@ -56,7 +51,7 @@ public String getSecurityName() { /** * Returns ISIN if description in "Name (ISIN)" format, null otherwise */ - public String getSecurityIsin() { + public @Nullable String getSecurityIsin() { return SecurityHelper.getSecurityIsin(security); } } diff --git a/src/main/java/ru/investbook/web/forms/model/SecurityEventCashFlowModel.java b/src/main/java/ru/investbook/web/forms/model/SecurityEventCashFlowModel.java index 4a25ae6b..d595ae06 100644 --- a/src/main/java/ru/investbook/web/forms/model/SecurityEventCashFlowModel.java +++ b/src/main/java/ru/investbook/web/forms/model/SecurityEventCashFlowModel.java @@ -19,12 +19,11 @@ package ru.investbook.web.forms.model; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.PositiveOrZero; import lombok.Data; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.springframework.format.annotation.DateTimeFormat; -import org.checkerframework.checker.nullness.qual.Nullable; import java.math.BigDecimal; import java.time.LocalDate; @@ -33,47 +32,34 @@ @Data public class SecurityEventCashFlowModel { - @Nullable - private Integer id; + private @Nullable Integer id; - @Nullable - private Integer taxId; + private @Nullable Integer taxId; - @NotEmpty - private String portfolio; + private @NotEmpty String portfolio; - @NotNull @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate date = LocalDate.now(); - @NotNull @DateTimeFormat(pattern = "HH:mm:ss") private LocalTime time = LocalTime.NOON; /** * In "name (isin)" or "contract-name" format */ - @NotEmpty - private String security; + private @NotEmpty String security; - @NotNull private int count; - @NotNull private CashFlowType type; - @NotNull private BigDecimal value; - @NotEmpty - private String valueCurrency = "RUB"; + private @NotEmpty String valueCurrency = "RUB"; - @Nullable - @PositiveOrZero - private BigDecimal tax; + private @Nullable @PositiveOrZero BigDecimal tax; - @Nullable - private String taxCurrency = "RUB"; + private @Nullable String taxCurrency = "RUB"; public void setValueCurrency(String currency) { this.valueCurrency = currency.toUpperCase(); @@ -83,7 +69,7 @@ public void setTaxCurrency(String currency) { this.taxCurrency = currency.toUpperCase(); } - public void setSecurity(String isin, String securityName, SecurityType securityType) { + public void setSecurity(@Nullable String isin, @Nullable String securityName, SecurityType securityType) { this.security = SecurityHelper.getSecurityDescription(isin, securityName, securityType); } @@ -97,12 +83,12 @@ public String getSecurityName() { /** * Returns ISIN if description in "Name (ISIN)" format, null otherwise */ - public String getSecurityIsin() { + public @Nullable String getSecurityIsin() { return SecurityHelper.getSecurityIsin(security); } public SecurityType getSecurityType() { - return switch(type) { + return switch (type) { case DIVIDEND -> SecurityType.SHARE; case ACCRUED_INTEREST, AMORTIZATION, REDEMPTION, COUPON -> SecurityType.BOND; case DERIVATIVE_PROFIT, DERIVATIVE_PRICE, DERIVATIVE_QUOTE -> SecurityType.DERIVATIVE; diff --git a/src/main/java/ru/investbook/web/forms/model/SecurityHelper.java b/src/main/java/ru/investbook/web/forms/model/SecurityHelper.java index 738d5337..18d6453d 100644 --- a/src/main/java/ru/investbook/web/forms/model/SecurityHelper.java +++ b/src/main/java/ru/investbook/web/forms/model/SecurityHelper.java @@ -18,6 +18,7 @@ package ru.investbook.web.forms.model; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.util.Assert; public class SecurityHelper { @@ -26,7 +27,7 @@ public class SecurityHelper { /** * For stock or bond "Name (ISIN)", for derivatives - code, for asset - securityName */ - public static String getSecurityDescription(String isin, String securityName, SecurityType securityType) { + public static String getSecurityDescription(@Nullable String isin, @Nullable String securityName, SecurityType securityType) { return switch (securityType) { case SHARE, BOND -> { Assert.isTrue(isin != null || securityName != null, "Отсутствует и ISIN, и наименование ЦБ"); @@ -34,7 +35,7 @@ public static String getSecurityDescription(String isin, String securityName, Se (isin == null ? "" : " (" + isin + ")"); } case DERIVATIVE, CURRENCY, ASSET -> { - Assert.isTrue(securityName != null, "Отсутствует тикер контракта или наименование произвольного актива"); + Assert.notNull(securityName, "Отсутствует тикер контракта или наименование произвольного актива"); yield securityName; } }; @@ -56,7 +57,7 @@ static String getSecurityName(String securityDescription, SecurityType securityT /** * Returns ISIN if description in "Name (ISIN)" format, null otherwise */ - static String getSecurityIsin(String securityDescription) { + static @Nullable String getSecurityIsin(String securityDescription) { securityDescription = securityDescription.trim(); int len = securityDescription.length(); return isSecurityDescriptionHasIsin(securityDescription) ? diff --git a/src/main/java/ru/investbook/web/forms/model/SecurityQuoteModel.java b/src/main/java/ru/investbook/web/forms/model/SecurityQuoteModel.java index 5e37b718..012474b3 100644 --- a/src/main/java/ru/investbook/web/forms/model/SecurityQuoteModel.java +++ b/src/main/java/ru/investbook/web/forms/model/SecurityQuoteModel.java @@ -19,7 +19,6 @@ package ru.investbook.web.forms.model; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.PositiveOrZero; import lombok.Data; @@ -35,34 +34,26 @@ @Data public class SecurityQuoteModel { - @Nullable - private Integer id; + private @Nullable Integer id; /** * In "name (isin)" or "contract-name" format */ - @NotEmpty - private String security; + private @NotEmpty String security; - @NotNull private SecurityType securityType; - @NotNull private Instant timestamp = LocalDate.now().atTime(LocalTime.NOON).atZone(systemDefault()).toInstant(); - @NotNull - @Positive - private BigDecimal quote; + private @Positive BigDecimal quote; - @Positive - private BigDecimal price; + private @Nullable @Positive BigDecimal price; - @PositiveOrZero - private BigDecimal accruedInterest; + private @Nullable @PositiveOrZero BigDecimal accruedInterest; - private String currency; + private @Nullable String currency; - public void setSecurity(String isin, String securityName, SecurityType securityType) { + public void setSecurity(@Nullable String isin, @Nullable String securityName, SecurityType securityType) { this.security = SecurityHelper.getSecurityDescription(isin, securityName, securityType); this.securityType = securityType; } @@ -77,7 +68,7 @@ public String getSecurityName() { /** * Returns ISIN if description in "Name (ISIN)" format, null otherwise */ - public String getSecurityIsin() { + public @Nullable String getSecurityIsin() { return SecurityHelper.getSecurityIsin(security); } } diff --git a/src/main/java/ru/investbook/web/forms/model/SplitModel.java b/src/main/java/ru/investbook/web/forms/model/SplitModel.java index c6b7fe3e..6a9d34b5 100644 --- a/src/main/java/ru/investbook/web/forms/model/SplitModel.java +++ b/src/main/java/ru/investbook/web/forms/model/SplitModel.java @@ -19,9 +19,9 @@ package ru.investbook.web.forms.model; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import lombok.Data; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDate; @@ -33,30 +33,22 @@ @Data public class SplitModel { - @NotEmpty - private String portfolio; + private @NotEmpty String portfolio; - @NotNull @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate date = LocalDate.now(); - @NotNull @DateTimeFormat(pattern = "HH:mm") private LocalTime time = LocalTime.NOON; /** * In "name (isin)" format */ - @NotEmpty - private String security; + private @NotEmpty String security; - @NotNull - @Positive - private int withdrawalCount; + private @Positive int withdrawalCount; - @NotNull - @Positive - private int depositCount; + private @Positive int depositCount; /** * Returns Name from template "Name (ISIN)" for stock and bond, code for derivative, securityName for asset @@ -68,7 +60,7 @@ public String getSecurityName() { /** * Returns ISIN if description in "Name (ISIN)" format, null otherwise */ - public String getSecurityIsin() { + public @Nullable String getSecurityIsin() { return SecurityHelper.getSecurityIsin(security); } diff --git a/src/main/java/ru/investbook/web/forms/model/TransactionModel.java b/src/main/java/ru/investbook/web/forms/model/TransactionModel.java index 7543cedc..c1d77404 100644 --- a/src/main/java/ru/investbook/web/forms/model/TransactionModel.java +++ b/src/main/java/ru/investbook/web/forms/model/TransactionModel.java @@ -19,22 +19,24 @@ package ru.investbook.web.forms.model; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.PositiveOrZero; import jakarta.xml.bind.DatatypeConverter; import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; import org.checkerframework.checker.nullness.qual.Nullable; -import org.springframework.util.StringUtils; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.util.Assert; import java.math.BigDecimal; -import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.LocalDate; import java.time.LocalTime; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; +import static org.springframework.util.StringUtils.hasText; + @Data public class TransactionModel { @@ -48,82 +50,59 @@ public class TransactionModel { } } - @Nullable - private Integer id; + private @Nullable Integer id; - @Nullable - private String tradeId; + private @Nullable String tradeId; - @NotEmpty - private String portfolio; + private @NotEmpty String portfolio; - @NotNull private Action action; - @NotNull @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate date = LocalDate.now(); - @NotNull @DateTimeFormat(pattern = "HH:mm:ss") private LocalTime time = LocalTime.NOON; /** * In "name (isin)" or "contract-name" format */ - @NotEmpty - private String security; + private @NotEmpty String security; - @NotNull private SecurityType securityType; - @NotNull - @Positive - private int count; + private @Positive int count; /** * Equals to null for security deposit and withdrawal */ - @Nullable - @Positive - private BigDecimal price; + private @Nullable @Positive BigDecimal price; - @Nullable - @PositiveOrZero - private BigDecimal accruedInterest; + private @Nullable @PositiveOrZero BigDecimal accruedInterest; /** * Price and accrued interest currency */ - @NotEmpty - private String priceCurrency = "RUB"; + private @NotEmpty String priceCurrency = "RUB"; - @Nullable - @Positive - private BigDecimal priceTick; + private @Nullable @Positive BigDecimal priceTick; - @Nullable - @Positive - private BigDecimal priceTickValue; + private @Nullable @Positive BigDecimal priceTickValue; - @Nullable - private String priceTickValueCurrency = "RUB"; + private @Nullable String priceTickValueCurrency = "RUB"; /** * May be null for security deposit and withdrawal */ - @Nullable - @PositiveOrZero - private BigDecimal fee; + private @Nullable @PositiveOrZero BigDecimal fee; - @NotEmpty - private String feeCurrency = "RUB"; + private @NotEmpty String feeCurrency = "RUB"; public enum Action { BUY, CELL } - public void setPriceTickValueCurrency(String priceTickValueCurrency) { + public void setPriceTickValueCurrency(@Nullable String priceTickValueCurrency) { if (priceTickValueCurrency != null) { this.priceTickValueCurrency = priceTickValueCurrency.toUpperCase(); } @@ -137,7 +116,7 @@ public void setFeeCurrency(String feeCurrency) { this.feeCurrency = feeCurrency.toUpperCase(); } - public void setSecurity(String isin, String securityName, SecurityType securityType) { + public void setSecurity(@Nullable String isin, @Nullable String securityName, SecurityType securityType) { this.security = SecurityHelper.getSecurityDescription(isin, securityName, securityType); this.securityType = securityType; } @@ -152,21 +131,23 @@ public String getSecurityName() { /** * Returns ISIN if description in "Name (ISIN)" format, null otherwise */ - public String getSecurityIsin() { + public @Nullable String getSecurityIsin() { return SecurityHelper.getSecurityIsin(security); } public String getTradeId() { - if (!StringUtils.hasText(tradeId)) { + if (!hasText(tradeId)) { setTradeId(createTradeId()); } - return tradeId; + @SuppressWarnings("nullness") + String nonNullTradeId = requireNonNull(tradeId, "Не задан trade-id"); + return nonNullTradeId; } private String createTradeId() { - if (portfolio == null || security == null || date == null || action == null) { - return null; - } + //noinspection ConstantValue + Assert.isTrue(portfolio != null && security != null && date != null && action != null, + "Невалидные данные, ошибка вычисления trade-id"); String string = portfolio.replaceAll(" ", "") + security.replaceAll(" ", "") + date + @@ -174,7 +155,7 @@ private String createTradeId() { count; synchronized (TransactionModel.class) { try { - md.update(string.getBytes(StandardCharsets.UTF_8)); + md.update(string.getBytes(UTF_8)); return DatatypeConverter.printHexBinary(md.digest()).toLowerCase(); } finally { md.reset(); @@ -183,8 +164,8 @@ private String createTradeId() { } public boolean hasDerivativeTickValue() { - return getPriceTick() != null && getPriceTick().floatValue() > 0.000001 && - getPriceTickValue() != null && getPriceTickValue().floatValue() > 0.000001 && - StringUtils.hasText(getPriceTickValueCurrency()); + return priceTick != null && priceTick.floatValue() > 0.000001 && + priceTickValue != null && priceTickValue.floatValue() > 0.000001 && + hasText(priceTickValueCurrency); } } diff --git a/src/main/java/ru/investbook/web/forms/service/EventCashFlowFormsService.java b/src/main/java/ru/investbook/web/forms/service/EventCashFlowFormsService.java index 86ad137f..fc3b8783 100644 --- a/src/main/java/ru/investbook/web/forms/service/EventCashFlowFormsService.java +++ b/src/main/java/ru/investbook/web/forms/service/EventCashFlowFormsService.java @@ -91,13 +91,15 @@ public void save(EventCashFlowModel e) { private void saveSecurityEventCashFlow(EventCashFlowModel e) { int savedSecurityId = securityRepositoryHelper.saveSecurity(requireNonNull(e.getAttachedSecurity())); + @SuppressWarnings("nullness") + int count = requireNonNull(e.getAttachedSecurity().getCount()); SecurityEventCashFlowEntity entity = securityEventCashFlowRepository.save( securityEventCashFlowConverter.toEntity(SecurityEventCashFlow.builder() // no id(), it is always the new object .portfolio(e.getPortfolio()) .timestamp(e.getDate().atTime(e.getTime()).atZone(zoneId).toInstant()) .security(savedSecurityId) - .count(e.getAttachedSecurity().getCount()) + .count(count) .eventType(e.getType()) .value(e.getValue()) .currency(e.getValueCurrency()) diff --git a/src/main/java/ru/investbook/web/forms/service/PortfolioPropertyFormsService.java b/src/main/java/ru/investbook/web/forms/service/PortfolioPropertyFormsService.java index 7e76089d..d06b20b6 100644 --- a/src/main/java/ru/investbook/web/forms/service/PortfolioPropertyFormsService.java +++ b/src/main/java/ru/investbook/web/forms/service/PortfolioPropertyFormsService.java @@ -42,11 +42,9 @@ import java.math.BigDecimal; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.util.Collection; import java.util.Optional; -import java.util.Set; -import static org.spacious_team.broker.pojo.PortfolioPropertyType.*; +import static org.spacious_team.broker.pojo.PortfolioPropertyType.valueOf; import static org.springframework.data.domain.Sort.Order.asc; import static org.springframework.data.domain.Sort.Order.desc; @@ -59,7 +57,6 @@ public class PortfolioPropertyFormsService { private final PortfolioRepository portfolioRepository; private final PortfolioPropertyConverter portfolioPropertyConverter; private final PortfolioConverter portfolioConverter; - private final Collection properties = Set.of(TOTAL_ASSETS_RUB.name(), TOTAL_ASSETS_USD.name()); @Transactional(readOnly = true) public Optional getById(Integer id) { diff --git a/src/main/java/ru/investbook/web/forms/service/SecurityEventCashFlowFormsService.java b/src/main/java/ru/investbook/web/forms/service/SecurityEventCashFlowFormsService.java index 10595226..eac5a220 100644 --- a/src/main/java/ru/investbook/web/forms/service/SecurityEventCashFlowFormsService.java +++ b/src/main/java/ru/investbook/web/forms/service/SecurityEventCashFlowFormsService.java @@ -94,7 +94,7 @@ public void save(SecurityEventCashFlowModel e) { .currency(e.getValueCurrency()) .build())); e.setId(entity.getId()); // used in view - if (e.getTax() != null && e.getTax().floatValue() > 0.001) { + if (e.getTax() != null && e.getTaxCurrency() != null && e.getTax().floatValue() > 0.001) { entity = securityEventCashFlowRepository.save(securityEventCashFlowConverter.toEntity( builder .id(e.getTaxId()) diff --git a/src/main/java/ru/investbook/web/forms/service/SecurityQuoteFormsService.java b/src/main/java/ru/investbook/web/forms/service/SecurityQuoteFormsService.java index 11b33b0d..729a2e6d 100644 --- a/src/main/java/ru/investbook/web/forms/service/SecurityQuoteFormsService.java +++ b/src/main/java/ru/investbook/web/forms/service/SecurityQuoteFormsService.java @@ -35,8 +35,10 @@ import ru.investbook.web.forms.model.SecurityType; import ru.investbook.web.forms.model.filter.SecurityQuoteFormFilterModel; +import java.math.BigDecimal; import java.util.Optional; +import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static org.springframework.data.domain.Sort.Order.asc; import static org.springframework.data.domain.Sort.Order.desc; @@ -70,12 +72,14 @@ public Page getPage(SecurityQuoteFormFilterModel filter) { @Transactional public void save(SecurityQuoteModel e) { int savedSecurityId = securityRepositoryHelper.saveSecurity(e); + @SuppressWarnings("nullness") + BigDecimal quote = requireNonNull(e.getQuote()); SecurityQuoteEntity entity = securityQuoteRepository.save( securityQuoteConverter.toEntity(SecurityQuote.builder() .id(e.getId()) .security(savedSecurityId) .timestamp(e.getTimestamp()) - .quote(e.getQuote()) + .quote(quote) .price(e.getPrice()) .accruedInterest(e.getAccruedInterest()) .currency(hasLength(e.getCurrency()) ? e.getCurrency() : null) diff --git a/src/main/java/ru/investbook/web/forms/service/SecurityRepositoryHelper.java b/src/main/java/ru/investbook/web/forms/service/SecurityRepositoryHelper.java index 780589de..c86c2630 100644 --- a/src/main/java/ru/investbook/web/forms/service/SecurityRepositoryHelper.java +++ b/src/main/java/ru/investbook/web/forms/service/SecurityRepositoryHelper.java @@ -22,7 +22,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.stereotype.Service; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; import ru.investbook.entity.SecurityEntity; import ru.investbook.repository.SecurityRepository; import ru.investbook.service.moex.MoexDerivativeCodeService; @@ -38,7 +37,9 @@ import java.util.Objects; import java.util.Optional; +import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; +import static org.springframework.util.StringUtils.hasLength; import static ru.investbook.entity.SecurityEntity.isinPattern; @@ -103,7 +104,7 @@ public int saveSecurity(SplitModel m) { /** * @return securityId from DB */ - private int saveSecurity(Integer securityId, String isin, String securityName, SecurityType securityType) { + private int saveSecurity(@Nullable Integer securityId, @Nullable String isin, String securityName, SecurityType securityType) { SecurityEntity security = ofNullable(securityId) .flatMap(securityRepository::findById) .or(() -> findSecurity(isin, securityName, securityType)) @@ -111,13 +112,13 @@ private int saveSecurity(Integer securityId, String isin, String securityName, S return saveSecurity(security, isin, securityName, securityType, true); } - private int saveSecurity(String isin, String securityName, SecurityType securityType) { + private int saveSecurity(@Nullable String isin, String securityName, SecurityType securityType) { SecurityEntity security = findSecurity(isin, securityName, securityType) .orElseGet(SecurityEntity::new); return saveSecurity(security, isin, securityName, securityType, false); } - private int saveSecurity(SecurityEntity security, String isin, String securityName, + private int saveSecurity(SecurityEntity security, @Nullable String isin, @Nullable String securityName, SecurityType securityType, boolean forceRewriteByEmptyIsin) { isin = validateIsin(isin); if (isin == null && !forceRewriteByEmptyIsin) { @@ -144,32 +145,33 @@ private int saveSecurity(SecurityEntity security, String isin, String securityNa return security.getId(); } - private Optional findSecurity(String isin, String securityName, SecurityType securityType) { + private Optional findSecurity(@Nullable String isin, @Nullable String securityName, SecurityType securityType) { isin = validateIsin(isin); securityName = Objects.equals(securityName, SecurityHelper.NULL_SECURITY_NAME) ? null : securityName; return switch (securityType) { case SHARE, BOND -> { Assert.isTrue(isin != null || securityName != null, "Отсутствует и ISIN, и наименование ЦБ"); - String name = securityName; + @Nullable String name = securityName; yield ofNullable(isin).flatMap(securityRepository::findByIsin) .or(() -> ofNullable(name).flatMap(securityRepository::findByTicker)) .or(() -> ofNullable(name).flatMap(securityRepository::findByName)); } case DERIVATIVE, CURRENCY -> { - Assert.notNull(securityName, "Отсутствует тикер контракта"); - yield securityRepository.findByTicker(securityName); + @SuppressWarnings("nullness") + String nonNullSecurityName = requireNonNull(securityName, "Отсутствует тикер контракта"); + yield securityRepository.findByTicker(nonNullSecurityName); } case ASSET -> { - Assert.notNull(securityName, "Отсутствует наименование произвольного актива"); - yield securityRepository.findByName(securityName); + @SuppressWarnings("nullness") + String nonNullSecurityName = requireNonNull(securityName, "Отсутствует наименование произвольного актива"); + yield securityRepository.findByName(nonNullSecurityName); } }; } - @Nullable - private String validateIsin(String isin) { - isin = StringUtils.hasLength(isin) ? isin : null; + private @Nullable String validateIsin(@Nullable String isin) { + isin = hasLength(isin) ? isin : null; if (isin != null && !isinPattern.matcher(isin).matches()) { throw new IllegalArgumentException("Невалидный ISIN: " + isin); } diff --git a/src/main/java/ru/investbook/web/forms/service/TransactionFormsService.java b/src/main/java/ru/investbook/web/forms/service/TransactionFormsService.java index 986fbd13..f9c0a8d9 100644 --- a/src/main/java/ru/investbook/web/forms/service/TransactionFormsService.java +++ b/src/main/java/ru/investbook/web/forms/service/TransactionFormsService.java @@ -19,6 +19,7 @@ package ru.investbook.web.forms.service; import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.Portfolio; import org.spacious_team.broker.report_parser.api.AbstractTransaction; @@ -137,7 +138,7 @@ public void save(TransactionModel tr) { .map(v -> v.multiply(multiplier)) .orElse(null)); case DERIVATIVE -> { - BigDecimal value = null; + @Nullable BigDecimal value = null; BigDecimal valueInPoints = tr.getPrice().multiply(multiplier); if (tr.hasDerivativeTickValue()) { value = valueInPoints @@ -167,6 +168,7 @@ public void save(TransactionModel tr) { }; } + @SuppressWarnings("DataFlowIssue") AbstractTransaction transaction = builder .id(tr.getId()) .tradeId(tr.getTradeId()) @@ -188,6 +190,7 @@ private int saveTransaction(AbstractTransaction transaction) { TransactionEntity transactionEntity = transactionRepository.save( transactionConverter.toEntity(transaction.getTransaction())); + //noinspection OptionalOfNullableMisuse Optional.ofNullable(transactionEntity.getId()).ifPresent(transactionCashFlowRepository::deleteByTransactionId); transactionCashFlowRepository.flush(); transaction.toBuilder() From 6f299953f4c433cc6ec0b05b173e0092c118bdcc Mon Sep 17 00:00:00 2001 From: vananiev Date: Mon, 30 Sep 2024 01:58:49 +0300 Subject: [PATCH 11/17] update maven-compiler-plugin to fix compilation error --- pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ccbb8029..53f984e7 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 24.2 22 1.18.34 - 3.42.0 + 3.47.0 @@ -262,7 +262,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 true true From ac9bc691977b01bfdd48094cca26e397d795a395 Mon Sep 17 00:00:00 2001 From: vananiev Date: Mon, 30 Sep 2024 03:29:10 +0300 Subject: [PATCH 12/17] fix nullable warn in api package --- .../api/AbstractEntityRepositoryService.java | 11 ++++--- .../api/AbstractRestController.java | 33 +++++++++++-------- .../api/EntityRepositoryService.java | 2 ++ .../api/EventCashFlowRestController.java | 7 ++-- .../ForeignExchangeRateRestController.java | 10 +++--- .../investbook/api/IssuerRestController.java | 7 ++-- .../api/PortfolioCashRestController.java | 7 ++-- .../api/PortfolioPropertyRestController.java | 7 ++-- .../api/PortfolioRestController.java | 6 ++-- .../SecurityDescriptionRestController.java | 6 ++-- .../SecurityEventCashFlowRestController.java | 11 +++---- .../api/SecurityQuoteRestController.java | 7 ++-- .../api/SecurityRestController.java | 9 +++-- .../TransactionCashFlowRestController.java | 30 +++++++++++++---- .../api/TransactionRestController.java | 9 +++-- .../SecurityDescriptionConverter.java | 8 +++-- .../ru/investbook/entity/IssuerEntity.java | 1 - .../repository/RepositoryHelper.java | 11 ++++--- 18 files changed, 108 insertions(+), 74 deletions(-) diff --git a/src/main/java/ru/investbook/api/AbstractEntityRepositoryService.java b/src/main/java/ru/investbook/api/AbstractEntityRepositoryService.java index fc5580f5..22e91599 100644 --- a/src/main/java/ru/investbook/api/AbstractEntityRepositoryService.java +++ b/src/main/java/ru/investbook/api/AbstractEntityRepositoryService.java @@ -22,6 +22,7 @@ import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.Session; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -93,7 +94,7 @@ public boolean insert(Pojo object) { log.error("Can't INSERT by optimized deprecated Hibernate method save(): {}", object, e); } } - Boolean result = transactionTemplateRequired.execute(_ -> createIfAbsent(object)); + @Nullable Boolean result = transactionTemplateRequired.execute(_ -> createIfAbsent(object)); return Boolean.TRUE.equals(result); } @@ -123,14 +124,14 @@ public CreateResult createIfAbsentAndGet(Pojo object) { * @implSpec Should be called in transaction */ private Optional createIfAbsentInternal(Pojo object) { - Entity entity = null; + @Nullable Entity entity = null; if (repository instanceof ConstraintAwareRepository caRepository) { entity = converter.toEntity(object); if (caRepository.exists(entity)) { return Optional.empty(); } } else { - ID id = getId(object); + @Nullable ID id = getId(object); if (id != null && existsById(id)) { return Optional.empty(); } @@ -153,13 +154,13 @@ private Optional createIfAbsentInternal(Pojo object) { * @implSpec Should be called in transaction */ private CreateResult createIfAbsentAndGetInternal(Pojo object) { - Entity entity = null; + @Nullable Entity entity = null; Optional selectedEntity; if (repository instanceof ConstraintAwareRepository caRepository) { entity = converter.toEntity(object); selectedEntity = caRepository.findBy(entity); } else { - ID id = getId(object); + @Nullable ID id = getId(object); selectedEntity = Optional.ofNullable(id) .flatMap(repository::findById); } diff --git a/src/main/java/ru/investbook/api/AbstractRestController.java b/src/main/java/ru/investbook/api/AbstractRestController.java index 03d99d12..4a52aad0 100644 --- a/src/main/java/ru/investbook/api/AbstractRestController.java +++ b/src/main/java/ru/investbook/api/AbstractRestController.java @@ -20,6 +20,7 @@ import jakarta.persistence.GeneratedValue; import lombok.SneakyThrows; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; @@ -32,9 +33,12 @@ import java.net.URI; import java.util.Objects; +import java.util.Optional; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.isNull; import static java.util.Objects.nonNull; +import static org.springframework.http.HttpStatus.CREATED; public abstract class AbstractRestController extends AbstractEntityRepositoryService { @@ -73,7 +77,7 @@ protected ResponseEntity post(Pojo object) { CreateResult result = createIfAbsentAndGet(object); Pojo savedObject = result.object(); if (result.created()) { - return createResponseWithLocationHeader(savedObject); + return createResponseWithOptionalLocationHeader(savedObject); } else { return createConflictResponse(savedObject); } @@ -85,10 +89,8 @@ protected ResponseEntity post(Pojo object) { @NonNull private ResponseEntity createConflictResponse(Pojo object) { ResponseEntity.BodyBuilder response = ResponseEntity.status(HttpStatus.CONFLICT); - if (getId(object) != null) { - URI locationURI = getLocationURI(object); - response.location(locationURI); - } + getLocationURI(object) + .ifPresent(response::location); return response.build(); } @@ -106,14 +108,14 @@ private ResponseEntity createConflictResponse(Pojo object) { @Transactional public ResponseEntity put(ID id, Pojo object) { try { - ID objectId = getId(object); + @Nullable ID objectId = getId(object); if (nonNull(objectId) && !Objects.equals(id, objectId)) { throw new BadRequestException("Идентификатор объекта, переданный в URI [" + id + "] и в теле " + "запроса [" + objectId + "] не совпадают"); } Pojo objectWithId = nonNull(objectId) ? object : updateId(id, object); return createAndGetIfAbsent(objectWithId) - .map(this::createResponseWithLocationHeader) + .map(this::createResponseWithOptionalLocationHeader) .orElseGet(() -> { createOrUpdate(objectWithId); return ResponseEntity.noContent().build(); @@ -134,16 +136,21 @@ public ResponseEntity delete(ID id) { /** * @return response entity with http CREATE status, Location http header and body */ - private ResponseEntity createResponseWithLocationHeader(Pojo object) { - URI locationURI = getLocationURI(object); - return ResponseEntity - .created(locationURI) + private ResponseEntity createResponseWithOptionalLocationHeader(Pojo object) { + return getLocationURI(object) + .map(ResponseEntity::created) + .orElseGet(() -> ResponseEntity.status(CREATED)) .build(); } @SneakyThrows - protected URI getLocationURI(Pojo object) { - return new URI(UriUtils.encodePath(getLocation() + "/" + getId(object), UTF_8)); + protected Optional getLocationURI(Pojo object) { + @Nullable ID id = getId(object); + if (isNull(id)) { + return Optional.empty(); + } + URI uri = new URI(UriUtils.encodePath(getLocation() + "/" + id, UTF_8)); + return Optional.of(uri); } protected abstract String getLocation(); diff --git a/src/main/java/ru/investbook/api/EntityRepositoryService.java b/src/main/java/ru/investbook/api/EntityRepositoryService.java index 1a802d5c..7b42b5ea 100644 --- a/src/main/java/ru/investbook/api/EntityRepositoryService.java +++ b/src/main/java/ru/investbook/api/EntityRepositoryService.java @@ -18,6 +18,7 @@ package ru.investbook.api; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -25,6 +26,7 @@ public interface EntityRepositoryService { + @Nullable ID getId(Pojo object); boolean existsById(ID id); diff --git a/src/main/java/ru/investbook/api/EventCashFlowRestController.java b/src/main/java/ru/investbook/api/EventCashFlowRestController.java index f8477e66..3de35821 100644 --- a/src/main/java/ru/investbook/api/EventCashFlowRestController.java +++ b/src/main/java/ru/investbook/api/EventCashFlowRestController.java @@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.EventCashFlow; import org.springdoc.core.converters.models.PageableAsQueryParam; import org.springframework.data.domain.Page; @@ -84,7 +85,7 @@ public ResponseEntity get(@PathVariable("id") @ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)), @ApiResponse(responseCode = "409"), @ApiResponse(responseCode = "500", content = @Content)}) - public ResponseEntity post(@Valid @RequestBody EventCashFlow event) { + public ResponseEntity post(@RequestBody @Valid EventCashFlow event) { return super.post(event); } @@ -97,8 +98,8 @@ public ResponseEntity post(@Valid @RequestBody EventCashFlow event) { public ResponseEntity put(@PathVariable("id") @Parameter(description = "Номер события") Integer id, - @Valid @RequestBody + @Valid EventCashFlow event) { return super.put(id, event); } @@ -115,7 +116,7 @@ public ResponseEntity delete(@PathVariable("id") } @Override - public Integer getId(EventCashFlow object) { + public @Nullable Integer getId(EventCashFlow object) { return object.getId(); } diff --git a/src/main/java/ru/investbook/api/ForeignExchangeRateRestController.java b/src/main/java/ru/investbook/api/ForeignExchangeRateRestController.java index 4b5d371c..605f9e5a 100644 --- a/src/main/java/ru/investbook/api/ForeignExchangeRateRestController.java +++ b/src/main/java/ru/investbook/api/ForeignExchangeRateRestController.java @@ -49,6 +49,7 @@ import java.net.URI; import java.time.LocalDate; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import static org.springframework.http.HttpHeaders.LOCATION; @@ -120,7 +121,7 @@ protected ResponseEntity get(@PathVariable("currency-pair") @ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)), @ApiResponse(responseCode = "409"), @ApiResponse(responseCode = "500", content = @Content)}) - public ResponseEntity post(@Valid @RequestBody ForeignExchangeRate object) { + public ResponseEntity post(@RequestBody @Valid ForeignExchangeRate object) { foreignExchangeRateService.invalidateCache(); return super.post(object); } @@ -141,8 +142,8 @@ public ResponseEntity put(@PathVariable("currency-pair") @Parameter(description = "Дата", example = "2021-01-23") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, - @Valid @RequestBody + @Valid ForeignExchangeRate object) { foreignExchangeRateService.invalidateCache(); return super.put(getId(currencyPair, date), object); @@ -189,8 +190,9 @@ protected ForeignExchangeRate updateId(ForeignExchangeRateEntityPk id, ForeignEx @Override @SneakyThrows - protected URI getLocationURI(ForeignExchangeRate object) { - return new URI(getLocation() + "/currency-pairs/" + object.getCurrencyPair() + "/dates/" + object.getDate()); + protected Optional getLocationURI(ForeignExchangeRate object) { + URI uri = new URI(getLocation() + "/currency-pairs/" + object.getCurrencyPair() + "/dates/" + object.getDate()); + return Optional.of(uri); } @Override diff --git a/src/main/java/ru/investbook/api/IssuerRestController.java b/src/main/java/ru/investbook/api/IssuerRestController.java index 2b4fc112..f6121f76 100644 --- a/src/main/java/ru/investbook/api/IssuerRestController.java +++ b/src/main/java/ru/investbook/api/IssuerRestController.java @@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.Issuer; import org.springdoc.core.converters.models.PageableAsQueryParam; import org.springframework.data.domain.Page; @@ -83,7 +84,7 @@ public ResponseEntity get(@PathVariable("id") @ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)), @ApiResponse(responseCode = "409"), @ApiResponse(responseCode = "500", content = @Content)}) - public ResponseEntity post(@Valid @RequestBody Issuer issuer) { + public ResponseEntity post(@RequestBody @Valid Issuer issuer) { return super.post(issuer); } @@ -96,8 +97,8 @@ public ResponseEntity post(@Valid @RequestBody Issuer issuer) { public ResponseEntity put(@PathVariable("id") @Parameter(description = "Внутренний идентификатор эмитента") Integer id, - @Valid @RequestBody + @Valid Issuer issuer) { return super.put(id, issuer); } @@ -114,7 +115,7 @@ public ResponseEntity delete(@PathVariable("id") } @Override - public Integer getId(Issuer object) { + public @Nullable Integer getId(Issuer object) { return object.getId(); } diff --git a/src/main/java/ru/investbook/api/PortfolioCashRestController.java b/src/main/java/ru/investbook/api/PortfolioCashRestController.java index 96d2996b..9862465e 100644 --- a/src/main/java/ru/investbook/api/PortfolioCashRestController.java +++ b/src/main/java/ru/investbook/api/PortfolioCashRestController.java @@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.PortfolioCash; import org.springdoc.core.converters.models.PageableAsQueryParam; import org.springframework.data.domain.Page; @@ -82,7 +83,7 @@ public ResponseEntity get(@PathVariable("id") @ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)), @ApiResponse(responseCode = "409"), @ApiResponse(responseCode = "500", content = @Content)}) - public ResponseEntity post(@Valid @RequestBody PortfolioCash property) { + public ResponseEntity post(@RequestBody @Valid PortfolioCash property) { return super.post(property); } @@ -95,8 +96,8 @@ public ResponseEntity post(@Valid @RequestBody PortfolioCash property) { public ResponseEntity put(@PathVariable("id") @Parameter(description = "Внутренний идентификатор записи") Integer id, - @Valid @RequestBody + @Valid PortfolioCash property) { return super.put(id, property); } @@ -113,7 +114,7 @@ public ResponseEntity delete(@PathVariable("id") } @Override - public Integer getId(PortfolioCash object) { + public @Nullable Integer getId(PortfolioCash object) { return object.getId(); } diff --git a/src/main/java/ru/investbook/api/PortfolioPropertyRestController.java b/src/main/java/ru/investbook/api/PortfolioPropertyRestController.java index 11fd8051..595c2aea 100644 --- a/src/main/java/ru/investbook/api/PortfolioPropertyRestController.java +++ b/src/main/java/ru/investbook/api/PortfolioPropertyRestController.java @@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.PortfolioProperty; import org.springdoc.core.converters.models.PageableAsQueryParam; import org.springframework.data.domain.Page; @@ -85,7 +86,7 @@ public ResponseEntity get(@PathVariable("id") @ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)), @ApiResponse(responseCode = "409"), @ApiResponse(responseCode = "500", content = @Content)}) - public ResponseEntity post(@Valid @RequestBody PortfolioProperty property) { + public ResponseEntity post(@RequestBody @Valid PortfolioProperty property) { return super.post(property); } @@ -99,8 +100,8 @@ public ResponseEntity post(@Valid @RequestBody PortfolioProperty property) public ResponseEntity put(@PathVariable("id") @Parameter(description = "Внутренний идентификатор записи") Integer id, - @Valid @RequestBody + @Valid PortfolioProperty property) { return super.put(id, property); } @@ -118,7 +119,7 @@ public ResponseEntity delete(@PathVariable("id") } @Override - public Integer getId(PortfolioProperty object) { + public @Nullable Integer getId(PortfolioProperty object) { return object.getId(); } diff --git a/src/main/java/ru/investbook/api/PortfolioRestController.java b/src/main/java/ru/investbook/api/PortfolioRestController.java index 429dd659..d86da423 100644 --- a/src/main/java/ru/investbook/api/PortfolioRestController.java +++ b/src/main/java/ru/investbook/api/PortfolioRestController.java @@ -48,11 +48,9 @@ @Tag(name = "Счета") @RequestMapping("/api/v1/portfolios") public class PortfolioRestController extends AbstractRestController { - private final PortfolioRepository repository; public PortfolioRestController(PortfolioRepository repository, PortfolioConverter converter) { super(repository, converter); - this.repository = repository; } @Override @@ -83,7 +81,7 @@ public ResponseEntity get(@PathVariable("id") @ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)), @ApiResponse(responseCode = "409"), @ApiResponse(responseCode = "500", content = @Content)}) - public ResponseEntity post(@Valid @RequestBody Portfolio object) { + public ResponseEntity post(@RequestBody @Valid Portfolio object) { return super.post(object); } @@ -96,8 +94,8 @@ public ResponseEntity post(@Valid @RequestBody Portfolio object) { public ResponseEntity put(@PathVariable("id") @Parameter(description = "Номер счета") String id, - @Valid @RequestBody + @Valid Portfolio object) { return super.put(id, object); } diff --git a/src/main/java/ru/investbook/api/SecurityDescriptionRestController.java b/src/main/java/ru/investbook/api/SecurityDescriptionRestController.java index e52563c1..546b1faf 100644 --- a/src/main/java/ru/investbook/api/SecurityDescriptionRestController.java +++ b/src/main/java/ru/investbook/api/SecurityDescriptionRestController.java @@ -48,11 +48,9 @@ @Tag(name = "Информация по инструментам", description = "Сектор экономики, эмитент") @RequestMapping("/api/v1/security-descriptions") public class SecurityDescriptionRestController extends AbstractRestController { - private final SecurityDescriptionRepository repository; public SecurityDescriptionRestController(SecurityDescriptionRepository repository, SecurityDescriptionConverter converter) { super(repository, converter); - this.repository = repository; } @Override @@ -89,7 +87,7 @@ public ResponseEntity get(@PathVariable("id") @ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)), @ApiResponse(responseCode = "409"), @ApiResponse(responseCode = "500", content = @Content)}) - public ResponseEntity post(@Valid @RequestBody SecurityDescription security) { + public ResponseEntity post(@RequestBody @Valid SecurityDescription security) { return super.post(security); } @@ -103,8 +101,8 @@ public ResponseEntity post(@Valid @RequestBody SecurityDescription securit public ResponseEntity put(@PathVariable("id") @Parameter(description = "Идентификатор", example = "123", required = true) Integer id, - @Valid @RequestBody + @Valid SecurityDescription security) { return super.put(id, security); } diff --git a/src/main/java/ru/investbook/api/SecurityEventCashFlowRestController.java b/src/main/java/ru/investbook/api/SecurityEventCashFlowRestController.java index 764d784d..ece6c652 100644 --- a/src/main/java/ru/investbook/api/SecurityEventCashFlowRestController.java +++ b/src/main/java/ru/investbook/api/SecurityEventCashFlowRestController.java @@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.SecurityEventCashFlow; import org.springdoc.core.converters.models.PageableAsQueryParam; import org.springframework.data.domain.Page; @@ -47,9 +48,7 @@ import static org.springframework.http.HttpHeaders.LOCATION; @RestController -@Tag(name = "События по бумаге", description = """ - Дивиденды, купоны, амортизации, вариационная маржа, комиссии, налоги - """) +@Tag(name = "События по бумаге", description = "Дивиденды, купоны, амортизации, вариационная маржа, комиссии, налоги") @RequestMapping("/api/v1/security-event-cash-flows") public class SecurityEventCashFlowRestController extends AbstractRestController { private final FifoPositionsFactory positionsFactory; @@ -89,7 +88,7 @@ public ResponseEntity get(@PathVariable("id") @ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)), @ApiResponse(responseCode = "409"), @ApiResponse(responseCode = "500", content = @Content)}) - public ResponseEntity post(@Valid @RequestBody SecurityEventCashFlow event) { + public ResponseEntity post(@RequestBody @Valid SecurityEventCashFlow event) { if (event.getEventType() == REDEMPTION) positionsFactory.invalidateCache(); return super.post(event); } @@ -103,8 +102,8 @@ public ResponseEntity post(@Valid @RequestBody SecurityEventCashFlow event public ResponseEntity put(@PathVariable("id") @Parameter(description = "Внутренний идентификатор выплаты") Integer id, - @Valid @RequestBody + @Valid SecurityEventCashFlow event) { if (event.getEventType() == REDEMPTION) positionsFactory.invalidateCache(); return super.put(id, event); @@ -123,7 +122,7 @@ public ResponseEntity delete(@PathVariable("id") } @Override - public Integer getId(SecurityEventCashFlow object) { + public @Nullable Integer getId(SecurityEventCashFlow object) { return object.getId(); } diff --git a/src/main/java/ru/investbook/api/SecurityQuoteRestController.java b/src/main/java/ru/investbook/api/SecurityQuoteRestController.java index e3623562..d1c4c0fd 100644 --- a/src/main/java/ru/investbook/api/SecurityQuoteRestController.java +++ b/src/main/java/ru/investbook/api/SecurityQuoteRestController.java @@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.SecurityQuote; import org.springdoc.core.converters.models.PageableAsQueryParam; import org.springframework.data.domain.Page; @@ -85,7 +86,7 @@ public ResponseEntity get(@PathVariable("id") @ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)), @ApiResponse(responseCode = "409"), @ApiResponse(responseCode = "500", content = @Content)}) - public ResponseEntity post(@Valid @RequestBody SecurityQuote quote) { + public ResponseEntity post(@RequestBody @Valid SecurityQuote quote) { return super.post(quote); } @@ -98,8 +99,8 @@ public ResponseEntity post(@Valid @RequestBody SecurityQuote quote) { public ResponseEntity put(@PathVariable("id") @Parameter(description = "Номер записи о котировке") Integer id, - @Valid @RequestBody + @Valid SecurityQuote quote) { return super.put(id, quote); } @@ -116,7 +117,7 @@ public ResponseEntity delete(@PathVariable("id") } @Override - public Integer getId(SecurityQuote object) { + public @Nullable Integer getId(SecurityQuote object) { return object.getId(); } diff --git a/src/main/java/ru/investbook/api/SecurityRestController.java b/src/main/java/ru/investbook/api/SecurityRestController.java index 2ac8fe49..da6203d7 100644 --- a/src/main/java/ru/investbook/api/SecurityRestController.java +++ b/src/main/java/ru/investbook/api/SecurityRestController.java @@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.Security; import org.springdoc.core.converters.models.PageableAsQueryParam; import org.springframework.data.domain.Page; @@ -48,11 +49,9 @@ @Tag(name = "Инструменты", description = "Акции, облигации, деривативы и валютные пары") @RequestMapping("/api/v1/securities") public class SecurityRestController extends AbstractRestController { - private final SecurityRepository repository; public SecurityRestController(SecurityRepository repository, SecurityConverter converter) { super(repository, converter); - this.repository = repository; } @Override @@ -88,7 +87,7 @@ public ResponseEntity get(@PathVariable("id") @ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)), @ApiResponse(responseCode = "409"), @ApiResponse(responseCode = "500", content = @Content)}) - public ResponseEntity post(@Valid @RequestBody Security security) { + public ResponseEntity post(@RequestBody @Valid Security security) { return super.post(security); } @@ -102,8 +101,8 @@ public ResponseEntity post(@Valid @RequestBody Security security) { public ResponseEntity put(@PathVariable("id") @Parameter(description = "Идентификатор", example = "123", required = true) Integer id, - @Valid @RequestBody + @Valid Security security) { return super.put(id, security); } @@ -121,7 +120,7 @@ public ResponseEntity delete(@PathVariable("id") } @Override - public Integer getId(Security object) { + public @Nullable Integer getId(Security object) { return object.getId(); } diff --git a/src/main/java/ru/investbook/api/TransactionCashFlowRestController.java b/src/main/java/ru/investbook/api/TransactionCashFlowRestController.java index 869b880d..ab7e9f13 100644 --- a/src/main/java/ru/investbook/api/TransactionCashFlowRestController.java +++ b/src/main/java/ru/investbook/api/TransactionCashFlowRestController.java @@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.Transaction; import org.spacious_team.broker.pojo.TransactionCashFlow; @@ -47,7 +48,9 @@ import ru.investbook.repository.TransactionCashFlowRepository; import java.util.List; +import java.util.stream.Stream; +import static java.util.Objects.isNull; import static org.springframework.http.HttpHeaders.LOCATION; @RestController @@ -76,12 +79,15 @@ public TransactionCashFlowRestController(TransactionCashFlowRepository repositor protected Page get( @RequestParam(value = "portfolio", required = false) @Parameter(description = "Номер счета") + @Nullable String portfolio, @RequestParam(value = "trade-id", required = false) @Parameter(description = "Номер сделки в системе учета брокера") + @Nullable String tradeId, @RequestParam(value = "event-type", required = false) @Parameter(description = "Тип (стоимость/комиссия/НКД)", example = "Смотреть API \"Типы событий\"") + @Nullable Integer eventType, @Parameter(hidden = true) Pageable pageable @@ -95,17 +101,27 @@ protected Page get( } private Page filterByEventType(Page transactions, - Integer eventType) { + @Nullable Integer eventType) { + List transactionCashFlows = transactions .stream() - .flatMap(transaction -> eventType == null ? - repository.findByTransactionId(transaction.getId()).stream() : - repository.findByTransactionIdAndCashFlowType(transaction.getId(), CashFlowType.valueOf(eventType)).stream()) + .flatMap(transaction -> findTransactionCashFlow(transaction, eventType)) .map(converter::fromEntity) .toList(); return new PageImpl<>(transactionCashFlows); } + private Stream findTransactionCashFlow(Transaction transaction, + @Nullable Integer eventType) { + @Nullable Integer id = transaction.getId(); + if (isNull(id)) { + return Stream.empty(); + } + return isNull(eventType) ? + repository.findByTransactionId(id).stream() : + repository.findByTransactionIdAndCashFlowType(id, CashFlowType.valueOf(eventType)).stream(); + } + @Override @GetMapping("{id}") @Operation(summary = "Отобразить одну", description = "Отобразить информацию о конкретной сделке", @@ -125,7 +141,7 @@ public ResponseEntity get(@PathVariable("id") @ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)), @ApiResponse(responseCode = "409"), @ApiResponse(responseCode = "500", content = @Content)}) - public ResponseEntity post(@Valid @RequestBody TransactionCashFlow object) { + public ResponseEntity post(@RequestBody @Valid TransactionCashFlow object) { return super.post(object); } @@ -142,8 +158,8 @@ public ResponseEntity post(@Valid @RequestBody TransactionCashFlow object) public ResponseEntity put(@PathVariable("id") @Parameter(description = "Внутренний идентификатор сделки") Integer id, - @Valid @RequestBody + @Valid TransactionCashFlow object) { return super.put(id, object); } @@ -166,7 +182,7 @@ public ResponseEntity delete(@PathVariable("id") } @Override - public Integer getId(TransactionCashFlow object) { + public @Nullable Integer getId(TransactionCashFlow object) { return object.getId(); } diff --git a/src/main/java/ru/investbook/api/TransactionRestController.java b/src/main/java/ru/investbook/api/TransactionRestController.java index 8020eefb..fbfce778 100644 --- a/src/main/java/ru/investbook/api/TransactionRestController.java +++ b/src/main/java/ru/investbook/api/TransactionRestController.java @@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.Transaction; import org.springdoc.core.converters.models.PageableAsQueryParam; import org.springframework.data.domain.Page; @@ -74,9 +75,11 @@ public TransactionRestController(TransactionRepository repository, @ApiResponse(responseCode = "500", content = @Content)}) public Page get(@RequestParam(value = "portfolio", required = false) @Parameter(description = "Идентификатор счета брокера") + @Nullable String portfolio, @RequestParam(value = "trade-id", required = false) @Parameter(description = "Номер сделки в системе учета брокера") + @Nullable String tradeId, @Parameter(hidden = true) Pageable pageable) { @@ -131,7 +134,7 @@ public ResponseEntity get(@PathVariable("id") @ApiResponse(responseCode = "201", headers = @Header(name = LOCATION)), @ApiResponse(responseCode = "409"), @ApiResponse(responseCode = "500", content = @Content)}) - public ResponseEntity post(@Valid @RequestBody Transaction object) { + public ResponseEntity post(@RequestBody @Valid Transaction object) { positionsFactory.invalidateCache(); return super.post(object); } @@ -149,8 +152,8 @@ public ResponseEntity post(@Valid @RequestBody Transaction object) { public ResponseEntity put(@PathVariable("id") @Parameter(description = "Внутренний идентификатор сделки") Integer id, - @Valid @RequestBody + @Valid Transaction object) { positionsFactory.invalidateCache(); return super.put(id, object); @@ -173,7 +176,7 @@ public ResponseEntity delete(@PathVariable("id") } @Override - public Integer getId(Transaction object) { + public @Nullable Integer getId(Transaction object) { return object.getId(); } diff --git a/src/main/java/ru/investbook/converter/SecurityDescriptionConverter.java b/src/main/java/ru/investbook/converter/SecurityDescriptionConverter.java index 70f6f7bc..3046a98a 100644 --- a/src/main/java/ru/investbook/converter/SecurityDescriptionConverter.java +++ b/src/main/java/ru/investbook/converter/SecurityDescriptionConverter.java @@ -35,8 +35,9 @@ public class SecurityDescriptionConverter implements EntityConverter Date: Mon, 30 Sep 2024 03:31:01 +0300 Subject: [PATCH 13/17] fix unit tests ci --- .github/workflows/unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 1b439176..21016435 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -22,7 +22,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: '21' + java-version: '22' distribution: 'liberica' cache: maven From d9e781830c0ebc86791f705160943acb2a7f811d Mon Sep 17 00:00:00 2001 From: vananiev Date: Mon, 30 Sep 2024 03:36:35 +0300 Subject: [PATCH 14/17] update jacoco version to 0.8.12 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 53f984e7..9c0877e2 100644 --- a/pom.xml +++ b/pom.xml @@ -373,7 +373,7 @@ org.jacoco jacoco-maven-plugin - 0.8.9 + 0.8.12 prepare-agent From 9698564d0aae2237f35c6f2b83f78a845d8fb0af Mon Sep 17 00:00:00 2001 From: vananiev Date: Mon, 30 Sep 2024 03:58:41 +0300 Subject: [PATCH 15/17] fix codecov --- .github/workflows/unit-tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 21016435..9b0c1078 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -30,7 +30,9 @@ jobs: run: mvn --batch-mode clean test - name: Test Coverage - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - name: SonarCloud Analyze run: > From e846826634100dca4f24bdcd09631d297c59b69e Mon Sep 17 00:00:00 2001 From: vananiev Date: Thu, 17 Oct 2024 23:23:26 +0300 Subject: [PATCH 16/17] fix nullness warn in openformat package --- .../openformat/v1_1_0/AccountPof.java | 13 +++++----- .../openformat/v1_1_0/AssetPof.java | 12 ++++----- .../openformat/v1_1_0/CashBalancesPof.java | 8 +++--- .../openformat/v1_1_0/CashFlowPof.java | 18 ++++++------- .../openformat/v1_1_0/PaymentPof.java | 26 +++++++++---------- .../openformat/v1_1_0/TransferPof.java | 22 ++++++++-------- .../openformat/v1_1_0/VndInvestbookPof.java | 10 +++---- 7 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java index f7c70836..7b7a9a7d 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/AccountPof.java @@ -27,11 +27,11 @@ import lombok.Value; import lombok.extern.jackson.Jacksonized; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.Portfolio; import org.spacious_team.broker.pojo.PortfolioProperty; import org.spacious_team.broker.pojo.PortfolioProperty.PortfolioPropertyBuilder; import org.spacious_team.broker.pojo.PortfolioPropertyType; -import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.entity.PortfolioEntity; import java.math.BigDecimal; @@ -57,24 +57,25 @@ public class AccountPof { private static final ThreadLocal idGenerator = ThreadLocal.withInitial(AtomicInteger::new); private static final ThreadLocal> accountNumberToIdMap = ThreadLocal.withInitial(HashMap::new); - @NotNull @JsonProperty("id") + @NotNull int id; - @Nullable + @JsonProperty("account-number") + @Nullable String accountNumber; - @NotNull @JsonProperty("type") + @NotNull AccountTypePof type; - @NotNull @JsonProperty("valuation") + @NotNull BigDecimal valuation; - @NotEmpty @JsonProperty("valuation-currency") + @NotEmpty String valuationCurrency; static void resetAccountIdGenerator() { diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/AssetPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/AssetPof.java index e5aff07b..028b31ce 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/AssetPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/AssetPof.java @@ -26,9 +26,9 @@ import lombok.Value; import lombok.extern.jackson.Jacksonized; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.Security; import org.spacious_team.broker.pojo.SecurityType; -import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.entity.SecurityEntity; import java.util.Optional; @@ -43,24 +43,24 @@ @Slf4j class AssetPof { - @NotNull @JsonProperty("id") + @NotNull int id; - @NotNull @JsonProperty("type") + @NotNull String type; - @Nullable @JsonProperty("symbol") + @Nullable String symbol; - @Nullable @JsonProperty("name") + @Nullable String name; - @Nullable @JsonProperty("isin") + @Nullable String isin; diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/CashBalancesPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/CashBalancesPof.java index 132956ff..c556b3c5 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/CashBalancesPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/CashBalancesPof.java @@ -48,24 +48,24 @@ @Slf4j public class CashBalancesPof { - @NotNull @JsonProperty("account") + @NotNull int account; - @NotNull @Builder.Default @JsonProperty("cash") + @NotNull Collection cash = Collections.emptyList(); @Value @JsonInclude(JsonInclude.Include.NON_NULL) private static class CashPof { - @NotNull @JsonProperty("value") + @NotNull BigDecimal value; - @NotEmpty @JsonProperty("currency") + @NotEmpty String currency; } diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/CashFlowPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/CashFlowPof.java index ce465e3e..4bc1b668 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/CashFlowPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/CashFlowPof.java @@ -27,9 +27,9 @@ import lombok.Value; import lombok.extern.jackson.Jacksonized; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.EventCashFlow; -import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.entity.EventCashFlowEntity; import java.math.BigDecimal; @@ -48,36 +48,36 @@ @Slf4j public class CashFlowPof { - @NotNull @JsonProperty("id") + @NotNull int id; - @Nullable @JsonProperty("flow-id") + @Nullable String flowId; - @NotNull @JsonProperty("account") + @NotNull int account; - @NotNull @JsonProperty("timestamp") + @NotNull long timestamp; - @NotNull @JsonProperty("amount") + @NotNull BigDecimal amount; - @NotEmpty @JsonProperty("currency") + @NotEmpty String currency; - @NotNull @JsonProperty("type") + @NotNull PaymentTypePof type; - @Nullable @JsonProperty("description") + @Nullable String description; static CashFlowPof of(EventCashFlowEntity cashFlow) { diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/PaymentPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/PaymentPof.java index e364c62c..0b1a6808 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/PaymentPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/PaymentPof.java @@ -29,10 +29,10 @@ import lombok.Value; import lombok.extern.jackson.Jacksonized; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.SecurityEventCashFlow; import org.spacious_team.broker.pojo.SecurityType; -import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.entity.SecurityEventCashFlowEntity; import ru.investbook.openformat.OpenFormatHelper; @@ -55,56 +55,56 @@ @Slf4j public class PaymentPof { - @NotNull @JsonProperty("id") + @NotNull int id; - @Nullable @JsonProperty("payment-id") + @Nullable String paymentId; - @NotNull @JsonProperty("account") + @NotNull int account; - @NotNull @Getter(AccessLevel.NONE) @JsonProperty("asset") + @NotNull int asset; - @NotNull @JsonProperty("type") + @NotNull PaymentTypePof type; /** * Поддерживаются дробные акции */ - @NotNull @JsonProperty("count") + @NotNull BigDecimal count; - @NotNull @JsonProperty("timestamp") + @NotNull long timestamp; - @NotNull @JsonProperty("amount") + @NotNull BigDecimal amount; - @NotEmpty @JsonProperty("currency") + @NotEmpty String currency; - @Nullable @JsonProperty("tax") + @Nullable BigDecimal tax; - @Nullable @JsonProperty("tax-currency") + @Nullable String taxCurrency; - @Nullable @JsonProperty("description") + @Nullable String description; static PaymentPof of(SecurityEventCashFlowEntity cashFlow, Optional tax) { diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/TransferPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/TransferPof.java index c1247597..48b58bf9 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/TransferPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/TransferPof.java @@ -28,9 +28,9 @@ import lombok.Value; import lombok.extern.jackson.Jacksonized; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.SecurityEventCashFlow; import org.spacious_team.broker.pojo.Transaction; -import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.entity.TransactionEntity; import java.math.BigDecimal; @@ -52,52 +52,52 @@ @JsonIgnoreProperties(ignoreUnknown = true) @Slf4j public class TransferPof { - @NotNull @JsonProperty("id") + @NotNull int id; /** * Если описывается сплит акций, то может иметь одинаковое значение для нескольких объектов, которое разрешается * {@link PortfolioOpenFormatPersister} */ - @Nullable @JsonProperty("transfer-id") + @Nullable String transferId; - @NotNull @JsonProperty("account") + @NotNull int account; - @NotNull @JsonProperty("timestamp") + @NotNull long timestamp; - @NotNull @JsonProperty("asset") @Getter(AccessLevel.NONE) + @NotNull int asset; /** * Поддерживаются дробные акции */ - @NotNull @JsonProperty("count") + @NotNull BigDecimal count; - @Nullable @JsonProperty("fee-account") + @Nullable Integer feeAccount; - @Nullable @JsonProperty("fee") + @Nullable BigDecimal fee; - @Nullable @JsonProperty("fee-currency") + @Nullable String feeCurrency; - @Nullable @JsonProperty("description") + @Nullable String description; static TransferPof of(TransactionEntity transaction) { diff --git a/src/main/java/ru/investbook/openformat/v1_1_0/VndInvestbookPof.java b/src/main/java/ru/investbook/openformat/v1_1_0/VndInvestbookPof.java index 10721de3..259b0438 100644 --- a/src/main/java/ru/investbook/openformat/v1_1_0/VndInvestbookPof.java +++ b/src/main/java/ru/investbook/openformat/v1_1_0/VndInvestbookPof.java @@ -40,31 +40,31 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class VndInvestbookPof { - @NotNull @JsonProperty("version") + @NotNull String version; - @NotNull @Builder.Default @JsonIgnoreProperties(value = {"id"}) @JsonProperty("portfolio-cash") + @NotNull Collection portfolioCash = Collections.emptySet(); - @NotNull @Builder.Default @JsonIgnoreProperties(value = {"id"}) @JsonProperty("portfolio-properties") + @NotNull Collection portfolioProperties = Collections.emptySet(); - @NotNull @Builder.Default @JsonIgnoreProperties(value = {"issuer"}) @JsonProperty("security-descriptions") + @NotNull Collection securityDescriptions = Collections.emptySet(); - @NotNull @Builder.Default @JsonIgnoreProperties(value = {"id"}) @JsonProperty("security-quotes") + @NotNull Collection securityQuotes = Collections.emptySet(); } From 77b66c4a9b880a0164a0af23ddf1b63896fa72c0 Mon Sep 17 00:00:00 2001 From: vananiev Date: Fri, 18 Oct 2024 02:15:04 +0300 Subject: [PATCH 17/17] add nullable annotations --- pom.xml | 16 +++++-- src/main/java/ru/investbook/package-info.java | 4 +- .../parser/TransactionValueAndFeeParser.java | 45 +++++++++---------- .../investbook/InvestbookCashFlowTable.java | 7 ++- .../InvestbookPortfolioCashTable.java | 3 +- .../InvestbookPortfolioPropertyTable.java | 3 +- ...bookSecurityDepositAndWithdrawalTable.java | 3 +- .../InvestbookSecurityEventCashFowTable.java | 3 +- .../InvestbookTransactionTable.java | 9 ++-- .../investbook/parser/psb/CashFlowTable.java | 10 +++-- .../ru/investbook/parser/psb/CashTable.java | 8 ++-- .../parser/psb/DerivativeCashFlowTable.java | 7 +-- .../parser/psb/DerivativeQuoteTable.java | 3 +- .../parser/psb/SecuritiesTable.java | 5 ++- .../ForeignExchangeTransactionTable.java | 2 +- .../parser/sber/SecurityHelper.java | 12 ++--- .../sber/cash_security/SberCashFlowTable.java | 6 ++- .../SberSecurityDepsitAndWithdrawalTable.java | 3 +- .../tinkoff/SecurityCodeAndIsinTable.java | 9 ++-- .../parser/uralsib/CashFlowTable.java | 8 ++-- .../investbook/parser/uralsib/CashTable.java | 5 ++- .../uralsib/DerivativeCashFlowTable.java | 8 ++-- .../parser/uralsib/DerivativeQuoteTable.java | 8 ++-- .../uralsib/DerivativeTransactionTable.java | 3 +- .../ForeignExchangeTransactionTable.java | 11 +++-- .../parser/uralsib/SecuritiesTable.java | 4 +- .../SecurityDepositAndWithdrawalTable.java | 3 +- .../uralsib/SecurityTransactionTable.java | 13 +++--- .../parser/vtb/CashFlowEventTable.java | 7 +-- 29 files changed, 134 insertions(+), 94 deletions(-) diff --git a/pom.xml b/pom.xml index 9c0877e2..eadb2e1c 100644 --- a/pom.xml +++ b/pom.xml @@ -62,10 +62,10 @@ - 24.2 + 24.2 22 1.18.34 - 3.47.0 + 3.48.1 @@ -91,6 +91,12 @@ compile + + + com.github.spacious-team + table-wrapper-api + f4572e7848 + @@ -244,7 +250,7 @@ nl.jqno.equalsverifier equalsverifier - 3.14.1 + 3.17.1 test @@ -254,7 +260,7 @@ maven-surefire-plugin - 2.22.2 + 3.5.1 @@ -297,6 +303,8 @@ -Awarns -AskipDefs=.*Test + -AskipDefs=V2022_1_0_1 + -AsuppressWarnings=type.anno.before.modifier -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED diff --git a/src/main/java/ru/investbook/package-info.java b/src/main/java/ru/investbook/package-info.java index b2d30b15..fd48bdf0 100644 --- a/src/main/java/ru/investbook/package-info.java +++ b/src/main/java/ru/investbook/package-info.java @@ -1,6 +1,6 @@ /* - * Table Wrapper Xml SpreadsheetML Impl - * Copyright (C) 2022 Spacious Team + * InvestBook + * Copyright (C) 2024 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/main/java/ru/investbook/parser/TransactionValueAndFeeParser.java b/src/main/java/ru/investbook/parser/TransactionValueAndFeeParser.java index 3756005b..b023d274 100644 --- a/src/main/java/ru/investbook/parser/TransactionValueAndFeeParser.java +++ b/src/main/java/ru/investbook/parser/TransactionValueAndFeeParser.java @@ -18,14 +18,13 @@ package ru.investbook.parser; -import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.RequiredArgsConstructor; import lombok.Value; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.table_wrapper.api.TableHeaderColumn; import org.spacious_team.table_wrapper.api.TableRow; -import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.stereotype.Component; import ru.investbook.report.ForeignExchangeRateService; @@ -60,10 +59,14 @@ public Arguments.ArgumentsBuilder argumentsBuilder() { */ public Result parse(Arguments arg) { - BigDecimal brokerFee = getFee(arg.row, arg.brokerFeeColumn).orElse(null); - BigDecimal marketFee = getFee(arg.row, arg.marketFeeColumn).orElse(null); - BigDecimal clearingFee = getFee(arg.row, arg.clearingFeeColumn).orElse(null); - BigDecimal stampDuty = getFee(arg.row, arg.stampDutyColumn).orElse(null); + @SuppressWarnings("DataFlowIssue") + @Nullable BigDecimal brokerFee = getFee(arg.row, arg.brokerFeeColumn).orElse(null); + @SuppressWarnings("DataFlowIssue") + @Nullable BigDecimal marketFee = getFee(arg.row, arg.marketFeeColumn).orElse(null); + @SuppressWarnings("DataFlowIssue") + @Nullable BigDecimal clearingFee = getFee(arg.row, arg.clearingFeeColumn).orElse(null); + @SuppressWarnings("DataFlowIssue") + @Nullable BigDecimal stampDuty = getFee(arg.row, arg.stampDutyColumn).orElse(null); BigDecimal value = arg.value; @@ -102,15 +105,18 @@ public Result parse(Arguments arg) { return new Result(value, valueCurrency, totalFee, totalFeeCurrency); } - private Optional getFee(TableRow row, TableHeaderColumn feeColumn) { + private Optional getFee(TableRow row, @Nullable TableHeaderColumn feeColumn) { + //noinspection DataFlowIssue,ReturnOfNull return Optional.ofNullable(feeColumn) .map(col -> row.getBigDecimalCellValueOrDefault(col, null)) .map(v -> Math.abs(v.floatValue()) > 1e-3 ? v : null); } private String getTotalFeeCurrency(Arguments arg, String valueCurrency, - BigDecimal brokerFee, BigDecimal marketFee, BigDecimal clearingFee, - BigDecimal stampDuty) { + @Nullable BigDecimal brokerFee, + @Nullable BigDecimal marketFee, + @Nullable BigDecimal clearingFee, + @Nullable BigDecimal stampDuty) { //noinspection ConstantConditions List feeCurrencies = Stream.of( @@ -127,13 +133,13 @@ private String getTotalFeeCurrency(Arguments arg, String valueCurrency, if (feeCurrencies.isEmpty()) { return valueCurrency; } else if (feeCurrencies.size() == 1) { - return feeCurrencies.get(0); + return feeCurrencies.getFirst(); } List currencies = feeCurrencies.stream() .filter(currency -> !Objects.equals(currency, valueCurrency)) .toList(); if (currencies.size() == 1) { - return currencies.get(0); + return currencies.getFirst(); } throw new IllegalArgumentException("Три разные валюты при проведении сделки не поддерживаются"); } @@ -146,9 +152,9 @@ private String filterCurrency(String currency) { * @throws NoSuchElementException если обменный курс не известен */ private BigDecimal addToTotalFee(BigDecimal totalFee, String totalFeeCurrency, - BigDecimal fee, TableHeaderColumn feeCurrencyColumn, + @Nullable BigDecimal fee, @Nullable TableHeaderColumn feeCurrencyColumn, Arguments arg) { - if (fee != null) { + if (fee != null && feeCurrencyColumn != null) { String feeCurrency = filterCurrency(arg.row.getStringCellValue(feeCurrencyColumn)); BigDecimal exchangeRate = arg.exchangeRateProvider .getExchangeRate(feeCurrency, totalFeeCurrency, arg.transactionInstant); @@ -158,9 +164,9 @@ private BigDecimal addToTotalFee(BigDecimal totalFee, String totalFeeCurrency, } private BigDecimal subtractFromValue(BigDecimal value, String valueCurrency, - BigDecimal fee, TableHeaderColumn feeCurrencyColumn, + @Nullable BigDecimal fee, @Nullable TableHeaderColumn feeCurrencyColumn, Arguments arg) { - if (fee == null) { + if (fee == null || feeCurrencyColumn == null) { return value; } String feeCurrency = filterCurrency(arg.row.getStringCellValue(feeCurrencyColumn)); @@ -184,20 +190,13 @@ public record Result(BigDecimal value, String valueCurrency, BigDecimal fee, Str @Value @Builder - private static class Arguments { - @NotNull + public static class Arguments { TableRow row; - @NotNull String portfolio; - @NotNull String tradeId; - @NotNull Instant transactionInstant; - @NotNull ExchangeRateProvider exchangeRateProvider; - @NotNull BigDecimal value; // отрицательное - для покупки, положительное - для продажи - @NotNull TableHeaderColumn valueCurrencyColumn; @Nullable TableHeaderColumn brokerFeeColumn; // положительное значение для списания комиссии diff --git a/src/main/java/ru/investbook/parser/investbook/InvestbookCashFlowTable.java b/src/main/java/ru/investbook/parser/investbook/InvestbookCashFlowTable.java index e3640e00..34849bf5 100644 --- a/src/main/java/ru/investbook/parser/investbook/InvestbookCashFlowTable.java +++ b/src/main/java/ru/investbook/parser/investbook/InvestbookCashFlowTable.java @@ -18,6 +18,7 @@ package ru.investbook.parser.investbook; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.EventCashFlow; import org.spacious_team.broker.report_parser.api.BrokerReport; @@ -37,7 +38,7 @@ protected InvestbookCashFlowTable(BrokerReport report) { } @Override - protected EventCashFlow parseRow(TableRow row) { + protected @Nullable EventCashFlow parseRow(TableRow row) { String operation = row.getStringCellValue(OPERATION).toLowerCase(); CashFlowType type; boolean negate; @@ -56,10 +57,12 @@ protected EventCashFlow parseRow(TableRow row) { } else { return null; } - BigDecimal value = Optional.ofNullable(row.getBigDecimalCellValueOrDefault(PRICE, null)) + @SuppressWarnings("DataFlowIssue") + @Nullable BigDecimal value = Optional.ofNullable(row.getBigDecimalCellValueOrDefault(PRICE, null)) .orElseGet(() -> row.getBigDecimalCellValue(FEE)) .abs(); if (negate) value = value.negate(); + @SuppressWarnings("DataFlowIssue") String currency = Optional.ofNullable(row.getStringCellValueOrDefault(CURRENCY, null)) .orElseGet(() -> row.getStringCellValue(FEE_CURRENCY)); return EventCashFlow.builder() diff --git a/src/main/java/ru/investbook/parser/investbook/InvestbookPortfolioCashTable.java b/src/main/java/ru/investbook/parser/investbook/InvestbookPortfolioCashTable.java index 5fca9a49..173a7dee 100644 --- a/src/main/java/ru/investbook/parser/investbook/InvestbookPortfolioCashTable.java +++ b/src/main/java/ru/investbook/parser/investbook/InvestbookPortfolioCashTable.java @@ -18,6 +18,7 @@ package ru.investbook.parser.investbook; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.PortfolioCash; import org.spacious_team.broker.report_parser.api.BrokerReport; import org.spacious_team.table_wrapper.api.TableRow; @@ -31,7 +32,7 @@ protected InvestbookPortfolioCashTable(BrokerReport report) { } @Override - protected PortfolioCash parseRow(TableRow row) { + protected @Nullable PortfolioCash parseRow(TableRow row) { String operation = row.getStringCellValue(OPERATION).toLowerCase(); if (!operation.contains("остаток")) { // Остаток денежных средств return null; diff --git a/src/main/java/ru/investbook/parser/investbook/InvestbookPortfolioPropertyTable.java b/src/main/java/ru/investbook/parser/investbook/InvestbookPortfolioPropertyTable.java index 63df3933..1639be31 100644 --- a/src/main/java/ru/investbook/parser/investbook/InvestbookPortfolioPropertyTable.java +++ b/src/main/java/ru/investbook/parser/investbook/InvestbookPortfolioPropertyTable.java @@ -18,6 +18,7 @@ package ru.investbook.parser.investbook; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.PortfolioProperty; import org.spacious_team.broker.pojo.PortfolioPropertyType; import org.spacious_team.broker.report_parser.api.BrokerReport; @@ -32,7 +33,7 @@ protected InvestbookPortfolioPropertyTable(BrokerReport report) { } @Override - protected PortfolioProperty parseRow(TableRow row) { + protected @Nullable PortfolioProperty parseRow(TableRow row) { String operation = row.getStringCellValue(OPERATION).toLowerCase(); if (!operation.contains("актив")) { // Оценка стоимости активов return null; diff --git a/src/main/java/ru/investbook/parser/investbook/InvestbookSecurityDepositAndWithdrawalTable.java b/src/main/java/ru/investbook/parser/investbook/InvestbookSecurityDepositAndWithdrawalTable.java index b49700d1..e8c937d9 100644 --- a/src/main/java/ru/investbook/parser/investbook/InvestbookSecurityDepositAndWithdrawalTable.java +++ b/src/main/java/ru/investbook/parser/investbook/InvestbookSecurityDepositAndWithdrawalTable.java @@ -18,6 +18,7 @@ package ru.investbook.parser.investbook; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.Security; import org.spacious_team.broker.pojo.SecurityType; import org.spacious_team.broker.report_parser.api.BrokerReport; @@ -42,7 +43,7 @@ protected InvestbookSecurityDepositAndWithdrawalTable(BrokerReport report, } @Override - protected SecurityTransaction parseRow(TableRow row) { + protected @Nullable SecurityTransaction parseRow(TableRow row) { boolean negate; String operation = row.getStringCellValue(OPERATION).toLowerCase(); if (operation.contains("зачисление")) { // Зачисление ЦБ diff --git a/src/main/java/ru/investbook/parser/investbook/InvestbookSecurityEventCashFowTable.java b/src/main/java/ru/investbook/parser/investbook/InvestbookSecurityEventCashFowTable.java index adfa6cc5..15312a53 100644 --- a/src/main/java/ru/investbook/parser/investbook/InvestbookSecurityEventCashFowTable.java +++ b/src/main/java/ru/investbook/parser/investbook/InvestbookSecurityEventCashFowTable.java @@ -18,6 +18,7 @@ package ru.investbook.parser.investbook; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.SecurityEventCashFlow; import org.spacious_team.broker.pojo.SecurityType; @@ -77,7 +78,7 @@ protected Collection parseRowToCollection(TableRow row) { .value(row.getBigDecimalCellValue(PRICE)) .currency(row.getStringCellValue(CURRENCY)); result.add(builder.build()); - BigDecimal tax = row.getBigDecimalCellValueOrDefault(FEE, null); + @Nullable BigDecimal tax = row.getBigDecimalCellValueOrDefault(FEE, null); if (tax != null && Math.abs(tax.floatValue()) > 0.001) { result.add(builder .eventType(CashFlowType.TAX) diff --git a/src/main/java/ru/investbook/parser/investbook/InvestbookTransactionTable.java b/src/main/java/ru/investbook/parser/investbook/InvestbookTransactionTable.java index 1b3d9d06..897d1319 100644 --- a/src/main/java/ru/investbook/parser/investbook/InvestbookTransactionTable.java +++ b/src/main/java/ru/investbook/parser/investbook/InvestbookTransactionTable.java @@ -18,6 +18,7 @@ package ru.investbook.parser.investbook; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.SecurityType; import org.spacious_team.broker.report_parser.api.AbstractTransaction; import org.spacious_team.broker.report_parser.api.AbstractTransaction.AbstractTransactionBuilder; @@ -45,7 +46,7 @@ protected InvestbookTransactionTable(BrokerReport report, } @Override - protected AbstractTransaction parseRow(TableRow row) { + protected @Nullable AbstractTransaction parseRow(TableRow row) { String operation = row.getStringCellValue(OPERATION).toLowerCase(); boolean isBuy; if (operation.contains("покупка")) { @@ -58,7 +59,7 @@ protected AbstractTransaction parseRow(TableRow row) { BigDecimal price = row.getBigDecimalCellValue(PRICE).abs(); int count = Math.abs(row.getIntCellValue(COUNT)); - BigDecimal value = getOptionalAmount(price, count, isBuy); + @Nullable BigDecimal value = getOptionalAmount(price, count, isBuy); String securityTickerNameOrIsin = row.getStringCellValue(TICKER_NAME_ISIN); SecurityType securityType = getSecurityTypeForTransaction(row, securityTickerNameOrIsin); @@ -97,7 +98,7 @@ protected AbstractTransaction parseRow(TableRow row) { .build(); } - private BigDecimal getOptionalAmount(BigDecimal price, int count, boolean isBuy) { + private @Nullable BigDecimal getOptionalAmount(@Nullable BigDecimal price, int count, boolean isBuy) { if (price == null) return null; BigDecimal value = price.multiply(BigDecimal.valueOf(count)); return isBuy ? value.negate() : value; @@ -105,7 +106,7 @@ private BigDecimal getOptionalAmount(BigDecimal price, int count, boolean isBuy) private SecurityType getSecurityTypeForTransaction(TableRow row, String securityTickerNameOrIsin) { SecurityType securityType; - String derivativePrice = row.getStringCellValueOrDefault(DERIVATIVE_PRICE_IN_CURRENCY, null); + @Nullable String derivativePrice = row.getStringCellValueOrDefault(DERIVATIVE_PRICE_IN_CURRENCY, null); if (derivativePrice != null) { securityType = SecurityType.DERIVATIVE; } else if (isCurrencyPair(securityTickerNameOrIsin)) { diff --git a/src/main/java/ru/investbook/parser/psb/CashFlowTable.java b/src/main/java/ru/investbook/parser/psb/CashFlowTable.java index bb631b44..57cdb380 100644 --- a/src/main/java/ru/investbook/parser/psb/CashFlowTable.java +++ b/src/main/java/ru/investbook/parser/psb/CashFlowTable.java @@ -20,6 +20,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.EventCashFlow; import org.spacious_team.table_wrapper.api.PatternTableColumn; @@ -44,9 +45,10 @@ public CashFlowTable(PsbBrokerReport report) { } @Override - protected EventCashFlow parseRow(TableRow row) { - String action = row.getStringCellValue(OPERATION); - action = String.valueOf(action).toLowerCase().trim(); + protected @Nullable EventCashFlow parseRow(TableRow row) { + String action = row.getStringCellValue(OPERATION) + .toLowerCase() + .trim(); CashFlowType type = CashFlowType.CASH; boolean isPositive; switch (action) { @@ -66,7 +68,7 @@ protected EventCashFlow parseRow(TableRow row) { if (type == CashFlowType.CASH && !row.getStringCellValue(DESCRIPTION).isEmpty()) { return null; // cash in/out records has no description } - String description = row.getStringCellValueOrDefault(DESCRIPTION, null); + @Nullable String description = row.getStringCellValueOrDefault(DESCRIPTION, null); BigDecimal value = row.getBigDecimalCellValue(VALUE); return EventCashFlow.builder() .portfolio(getReport().getPortfolio()) diff --git a/src/main/java/ru/investbook/parser/psb/CashTable.java b/src/main/java/ru/investbook/parser/psb/CashTable.java index 958f0802..087f9b15 100644 --- a/src/main/java/ru/investbook/parser/psb/CashTable.java +++ b/src/main/java/ru/investbook/parser/psb/CashTable.java @@ -20,6 +20,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.PortfolioCash; import org.spacious_team.table_wrapper.api.PatternTableColumn; import org.spacious_team.table_wrapper.api.TableColumn; @@ -41,7 +42,7 @@ public CashTable(PsbBrokerReport report) { } @Override - protected PortfolioCash parseRow(TableRow row) { + protected @Nullable PortfolioCash parseRow(TableRow row) { return row.rowContains(INVALID_TEXT) ? null : PortfolioCash.builder() .portfolio(getReport().getPortfolio()) @@ -52,14 +53,15 @@ protected PortfolioCash parseRow(TableRow row) { .build(); } + @Getter enum CashTableHeader implements TableHeaderColumn { SECTION("сектор"), VALUE("плановый исходящий остаток"), CURRENCY("валюта"); - @Getter private final TableColumn column; - CashTableHeader(String ... words) { + + CashTableHeader(String... words) { this.column = PatternTableColumn.of(words); } } diff --git a/src/main/java/ru/investbook/parser/psb/DerivativeCashFlowTable.java b/src/main/java/ru/investbook/parser/psb/DerivativeCashFlowTable.java index 0b1f803d..6c9cc663 100644 --- a/src/main/java/ru/investbook/parser/psb/DerivativeCashFlowTable.java +++ b/src/main/java/ru/investbook/parser/psb/DerivativeCashFlowTable.java @@ -21,6 +21,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.SecurityEventCashFlow; import org.spacious_team.table_wrapper.api.PatternTableColumn; @@ -80,7 +81,7 @@ private static AbstractMap.SimpleEntry getCount(TableRow row) { } @Override - protected SecurityEventCashFlow parseRow(TableRow row) { + protected @Nullable SecurityEventCashFlow parseRow(TableRow row) { BigDecimal value = row.getBigDecimalCellValue(DerivativeCashFlowTableHeader.INCOMING) .subtract(row.getBigDecimalCellValue(DerivativeCashFlowTableHeader.OUTGOING)); SecurityEventCashFlow.SecurityEventCashFlowBuilder builder = SecurityEventCashFlow.builder() @@ -112,6 +113,7 @@ protected SecurityEventCashFlow parseRow(TableRow row) { } } + @Getter enum ContractCountTableHeader implements TableHeaderColumn { CONTRACT("^контракт$"), INCOMING("входящий остаток"), @@ -122,7 +124,6 @@ enum ContractCountTableHeader implements TableHeaderColumn { PRICE_TICK("шаг цены"), PRICE_TICK_VALUE("стоимость шага цены"); - @Getter private final TableColumn column; ContractCountTableHeader(String... words) { @@ -130,6 +131,7 @@ enum ContractCountTableHeader implements TableHeaderColumn { } } + @Getter enum DerivativeCashFlowTableHeader implements TableHeaderColumn { DATE("дата"), CONTRACT("№", "контракт"), @@ -137,7 +139,6 @@ enum DerivativeCashFlowTableHeader implements TableHeaderColumn { INCOMING("зачислено"), OUTGOING("списано"); - @Getter private final TableColumn column; DerivativeCashFlowTableHeader(String... words) { diff --git a/src/main/java/ru/investbook/parser/psb/DerivativeQuoteTable.java b/src/main/java/ru/investbook/parser/psb/DerivativeQuoteTable.java index e1ced79e..916a76d6 100644 --- a/src/main/java/ru/investbook/parser/psb/DerivativeQuoteTable.java +++ b/src/main/java/ru/investbook/parser/psb/DerivativeQuoteTable.java @@ -18,6 +18,7 @@ package ru.investbook.parser.psb; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.SecurityQuote; import org.spacious_team.table_wrapper.api.TableRow; import ru.investbook.parser.SingleAbstractReportTable; @@ -37,7 +38,7 @@ public DerivativeQuoteTable(PsbBrokerReport report) { } @Override - protected SecurityQuote parseRow(TableRow row) { + protected @Nullable SecurityQuote parseRow(TableRow row) { BigDecimal price = row.getBigDecimalCellValue(PRICE); BigDecimal tickValue = row.getBigDecimalCellValue(PRICE_TICK_VALUE); if (price.compareTo(minValue) < 0 || tickValue.compareTo(minValue) < 0) { diff --git a/src/main/java/ru/investbook/parser/psb/SecuritiesTable.java b/src/main/java/ru/investbook/parser/psb/SecuritiesTable.java index 09b5486a..517bcbfd 100644 --- a/src/main/java/ru/investbook/parser/psb/SecuritiesTable.java +++ b/src/main/java/ru/investbook/parser/psb/SecuritiesTable.java @@ -20,6 +20,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.Security; import org.spacious_team.broker.pojo.Security.SecurityBuilder; import org.spacious_team.broker.pojo.SecurityType; @@ -43,7 +44,7 @@ public SecuritiesTable(PsbBrokerReport report) { } @Override - protected Security parseRow(TableRow row) { + protected @Nullable Security parseRow(TableRow row) { if (row.rowContains(INVALID_TEXT)) { return null; } @@ -56,6 +57,7 @@ protected Security parseRow(TableRow row) { return security.id(securityId).build(); } + @Getter enum SecuritiesTableHeader implements TableHeaderColumn { NAME("наименование"), ISIN("isin"), @@ -68,7 +70,6 @@ enum SecuritiesTableHeader implements TableHeaderColumn { FACEUNIT("валюта бумаги"), CURRENCY("валюта цены"); - @Getter private final TableColumn column; SecuritiesTableHeader(String... words) { diff --git a/src/main/java/ru/investbook/parser/psb/foreignmarket/ForeignExchangeTransactionTable.java b/src/main/java/ru/investbook/parser/psb/foreignmarket/ForeignExchangeTransactionTable.java index f79f1281..2fd0766e 100644 --- a/src/main/java/ru/investbook/parser/psb/foreignmarket/ForeignExchangeTransactionTable.java +++ b/src/main/java/ru/investbook/parser/psb/foreignmarket/ForeignExchangeTransactionTable.java @@ -68,6 +68,7 @@ protected ForeignExchangeTransaction parseRow(TableRow row) { .build(); } + @Getter enum FxTransactionTableHeader implements TableHeaderColumn { TRADE_ID("номер сделки"), DATE_TIME("дата", "заключения сделки"), // учет по дате сделки, а не дате исполнения, чтобы учесть неисполненные сделки @@ -78,7 +79,6 @@ enum FxTransactionTableHeader implements TableHeaderColumn { MARKET_COMMISSION("комиссия", "биржи", "руб"), POSITION_SWAP("перенос", "позиции"); - @Getter private final TableColumn column; FxTransactionTableHeader(String... words) { diff --git a/src/main/java/ru/investbook/parser/sber/SecurityHelper.java b/src/main/java/ru/investbook/parser/sber/SecurityHelper.java index 7592dd0e..b0525863 100644 --- a/src/main/java/ru/investbook/parser/sber/SecurityHelper.java +++ b/src/main/java/ru/investbook/parser/sber/SecurityHelper.java @@ -19,6 +19,7 @@ package ru.investbook.parser.sber; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.Security; import org.spacious_team.broker.pojo.SecurityType; import org.springframework.util.StringUtils; @@ -30,8 +31,9 @@ @Slf4j public class SecurityHelper { - public static Security getSecurity(String code, String securityName, String section, String type, SecurityRegistrar securityRegistrar) { - String securityId = getSecurityId(code, section); + public static Security getSecurity(String code, @Nullable String securityName, String section, @Nullable String type, + SecurityRegistrar securityRegistrar) { + @Nullable String securityId = getSecurityId(code, section); SecurityType securityType = getSecurityType(section, type); Security.SecurityBuilder security = Security.builder().type(securityType); int id = -1; @@ -64,7 +66,7 @@ public static Security getSecurity(String code, String securityName, String sect * @param nameAndIsin in format "\s*()" * @return isin for stock or bond, nameAndIsin for others */ - private static String getSecurityId(String nameAndIsin, String section) { + private static @Nullable String getSecurityId(String nameAndIsin, String section) { int start = nameAndIsin.indexOf('(') + 1; int end = nameAndIsin.indexOf(')'); String id = nameAndIsin.substring(start, (end == -1) ? nameAndIsin.length() : end); @@ -74,7 +76,7 @@ private static String getSecurityId(String nameAndIsin, String section) { return id; } - private static String getValidIsinOrNull(String isin) { + private static @Nullable String getValidIsinOrNull(String isin) { return StringUtils.hasLength(isin) && isinPattern.matcher(isin).matches() ? isin : null; @@ -85,7 +87,7 @@ public static String getSecurityName(String nameAndIsin) { return (start == -1) ? nameAndIsin : nameAndIsin.substring(0, start).trim(); } - public static SecurityType getSecurityType(String section, String securityType) { + public static SecurityType getSecurityType(String section, @Nullable String securityType) { if ("Фондовый рынок".equalsIgnoreCase(section)) { if (securityType == null) return SecurityType.STOCK_OR_BOND; return switch (securityType.toLowerCase()) { diff --git a/src/main/java/ru/investbook/parser/sber/cash_security/SberCashFlowTable.java b/src/main/java/ru/investbook/parser/sber/cash_security/SberCashFlowTable.java index 448fb5ac..3534f1f2 100644 --- a/src/main/java/ru/investbook/parser/sber/cash_security/SberCashFlowTable.java +++ b/src/main/java/ru/investbook/parser/sber/cash_security/SberCashFlowTable.java @@ -20,6 +20,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.EventCashFlow; import org.spacious_team.broker.report_parser.api.AbstractReportTable; @@ -43,7 +44,7 @@ protected SberCashFlowTable(SberCashBrokerReport report) { } @Override - protected EventCashFlow parseRow(TableRow row) { + protected @Nullable EventCashFlow parseRow(TableRow row) { if (!"Исполнено".equalsIgnoreCase(row.getStringCellValueOrDefault(STATUS, null))) { return null; } @@ -88,6 +89,7 @@ protected EventCashFlow parseRow(TableRow row) { .build(); } + @Getter enum SberCashFlowTableHeader implements TableHeaderColumn { PORTFOLIO("Номер договора"), DATE_TIME("Дата исполнения поручения"), @@ -99,8 +101,8 @@ enum SberCashFlowTableHeader implements TableHeaderColumn { DESCRIPTION("Содержание операции"), STATUS("Статус"); - @Getter private final TableColumn column; + SberCashFlowTableHeader(String words) { this.column = PatternTableColumn.of(words); } diff --git a/src/main/java/ru/investbook/parser/sber/cash_security/SberSecurityDepsitAndWithdrawalTable.java b/src/main/java/ru/investbook/parser/sber/cash_security/SberSecurityDepsitAndWithdrawalTable.java index fc3ff293..750572c7 100644 --- a/src/main/java/ru/investbook/parser/sber/cash_security/SberSecurityDepsitAndWithdrawalTable.java +++ b/src/main/java/ru/investbook/parser/sber/cash_security/SberSecurityDepsitAndWithdrawalTable.java @@ -20,6 +20,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.Security; import org.spacious_team.broker.report_parser.api.AbstractReportTable; import org.spacious_team.broker.report_parser.api.AbstractTransaction; @@ -49,7 +50,7 @@ protected SberSecurityDepsitAndWithdrawalTable(SberSecurityDepositBrokerReport r } @Override - protected SecurityTransaction parseRow(TableRow row) { + protected @Nullable SecurityTransaction parseRow(TableRow row) { if (!"Исполнено".equalsIgnoreCase(row.getStringCellValueOrDefault(STATUS, null))) { return null; } else if ("Погашение ценной бумаги".equalsIgnoreCase(row.getStringCellValueOrDefault(DESCRIPTION, null))) { diff --git a/src/main/java/ru/investbook/parser/tinkoff/SecurityCodeAndIsinTable.java b/src/main/java/ru/investbook/parser/tinkoff/SecurityCodeAndIsinTable.java index f424d65d..51897ca2 100644 --- a/src/main/java/ru/investbook/parser/tinkoff/SecurityCodeAndIsinTable.java +++ b/src/main/java/ru/investbook/parser/tinkoff/SecurityCodeAndIsinTable.java @@ -21,6 +21,7 @@ import jakarta.validation.constraints.NotNull; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.SecurityType; import org.spacious_team.broker.report_parser.api.AbstractReportTable; import org.spacious_team.broker.report_parser.api.BrokerReport; @@ -54,8 +55,8 @@ protected SecurityCodeAndIsinTable(BrokerReport report) { } @Override - protected Void parseRow(TableRow row) { - String code = row.getStringCellValueOrDefault(CODE, null); + protected @Nullable Void parseRow(TableRow row) { + @Nullable String code = row.getStringCellValueOrDefault(CODE, null); if (hasLength(code) && !code.contains("Код актива")) { // exclude table's empty row // если колонка ISIN отсутствует, то ISIN используется в отчете вместо кода (SBERP) String isin = row.getStringCellValueOrDefault(ISIN, code); @@ -72,12 +73,12 @@ protected Void parseRow(TableRow row) { } codeToType.put(code, securityType); - BigDecimal faceValue = row.getBigDecimalCellValueOrDefault(FACE_VALUE, null); + @Nullable BigDecimal faceValue = row.getBigDecimalCellValueOrDefault(FACE_VALUE, null); if (faceValue != null) { codeToFaceValue.put(code, faceValue); } - String shortName = row.getStringCellValueOrDefault(SHORT_NAME, null); + @Nullable String shortName = row.getStringCellValueOrDefault(SHORT_NAME, null); if (hasLength(shortName)) { shortNameToCode.put(shortName, code); } diff --git a/src/main/java/ru/investbook/parser/uralsib/CashFlowTable.java b/src/main/java/ru/investbook/parser/uralsib/CashFlowTable.java index adf67f4c..983a35c6 100644 --- a/src/main/java/ru/investbook/parser/uralsib/CashFlowTable.java +++ b/src/main/java/ru/investbook/parser/uralsib/CashFlowTable.java @@ -19,6 +19,7 @@ package ru.investbook.parser.uralsib; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.EventCashFlow; import org.spacious_team.table_wrapper.api.TableRow; @@ -43,9 +44,10 @@ public CashFlowTable(UralsibBrokerReport report) { } @Override - protected EventCashFlow parseRow(TableRow row) { - String action = row.getStringCellValue(OPERATION); - action = String.valueOf(action).toLowerCase().trim(); + protected @Nullable EventCashFlow parseRow(TableRow row) { + String action = row.getStringCellValue(OPERATION) + .toLowerCase() + .trim(); String description = row.getStringCellValueOrDefault(DESCRIPTION, ""); CashFlowType type; switch (action) { diff --git a/src/main/java/ru/investbook/parser/uralsib/CashTable.java b/src/main/java/ru/investbook/parser/uralsib/CashTable.java index 7645c88c..a3f16e5e 100644 --- a/src/main/java/ru/investbook/parser/uralsib/CashTable.java +++ b/src/main/java/ru/investbook/parser/uralsib/CashTable.java @@ -50,13 +50,14 @@ protected PortfolioCash parseRow(TableRow row) { .build(); } + @Getter enum CashTableHeader implements TableHeaderColumn { VALUE("исходящий остаток"), CURRENCY("код валюты"); - @Getter private final TableColumn column; - CashTableHeader(String ... words) { + + CashTableHeader(String... words) { this.column = PatternTableColumn.of(words); } } diff --git a/src/main/java/ru/investbook/parser/uralsib/DerivativeCashFlowTable.java b/src/main/java/ru/investbook/parser/uralsib/DerivativeCashFlowTable.java index 2be3f1fa..7492aa2b 100644 --- a/src/main/java/ru/investbook/parser/uralsib/DerivativeCashFlowTable.java +++ b/src/main/java/ru/investbook/parser/uralsib/DerivativeCashFlowTable.java @@ -19,6 +19,7 @@ package ru.investbook.parser.uralsib; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.broker.pojo.SecurityEventCashFlow; import org.spacious_team.table_wrapper.api.TableRow; @@ -39,9 +40,10 @@ public DerivativeCashFlowTable(UralsibBrokerReport report) { super(report, PaymentsTable.TABLE_NAME, "", PaymentsTable.PaymentsTableHeader.class); } - protected SecurityEventCashFlow parseRow(TableRow row) { - String action = row.getStringCellValue(OPERATION); - action = String.valueOf(action).toLowerCase().trim(); + protected @Nullable SecurityEventCashFlow parseRow(TableRow row) { + String action = row.getStringCellValue(OPERATION) + .toLowerCase() + .trim(); if (!action.equalsIgnoreCase("вариационная маржа")) { return null; } diff --git a/src/main/java/ru/investbook/parser/uralsib/DerivativeQuoteTable.java b/src/main/java/ru/investbook/parser/uralsib/DerivativeQuoteTable.java index 4a079c44..c715a1da 100644 --- a/src/main/java/ru/investbook/parser/uralsib/DerivativeQuoteTable.java +++ b/src/main/java/ru/investbook/parser/uralsib/DerivativeQuoteTable.java @@ -19,6 +19,7 @@ package ru.investbook.parser.uralsib; import lombok.Getter; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.SecurityQuote; import org.spacious_team.table_wrapper.api.PatternTableColumn; import org.spacious_team.table_wrapper.api.TableColumn; @@ -42,8 +43,8 @@ protected DerivativeQuoteTable(UralsibBrokerReport report) { } @Override - protected SecurityQuote parseRow(TableRow row) { - BigDecimal quote = row.getBigDecimalCellValueOrDefault(QUOTE, null); + protected @Nullable SecurityQuote parseRow(TableRow row) { + @Nullable BigDecimal quote = row.getBigDecimalCellValueOrDefault(QUOTE, null); if (quote == null || quote.compareTo(minValue) < 0) { return null; } @@ -56,6 +57,7 @@ protected SecurityQuote parseRow(TableRow row) { .build(); } + @Getter enum ContractCountTableHeader implements TableHeaderColumn { CONTRACT("наименование контракта"), INCOMING("входящий остаток"), @@ -64,8 +66,8 @@ enum ContractCountTableHeader implements TableHeaderColumn { CELL("списано"), QUOTE("расчетная цена"); - @Getter private final TableColumn column; + ContractCountTableHeader(String... words) { this.column = PatternTableColumn.of(words); } diff --git a/src/main/java/ru/investbook/parser/uralsib/DerivativeTransactionTable.java b/src/main/java/ru/investbook/parser/uralsib/DerivativeTransactionTable.java index 48a33b1d..513a0fa6 100644 --- a/src/main/java/ru/investbook/parser/uralsib/DerivativeTransactionTable.java +++ b/src/main/java/ru/investbook/parser/uralsib/DerivativeTransactionTable.java @@ -21,6 +21,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.report_parser.api.DerivativeTransaction; import org.spacious_team.table_wrapper.api.AnyOfTableColumn; import org.spacious_team.table_wrapper.api.OptionalTableColumn; @@ -55,7 +56,7 @@ protected DerivativeTransactionTable(UralsibBrokerReport report, String tableNam } @Override - protected DerivativeTransaction parseRow(TableRow row) { + protected @Nullable DerivativeTransaction parseRow(TableRow row) { if (expirationTableReached) return null; String tradeId = SecurityTransactionTable.getTradeId(row, TRANSACTION); if (tradeId == null) { diff --git a/src/main/java/ru/investbook/parser/uralsib/ForeignExchangeTransactionTable.java b/src/main/java/ru/investbook/parser/uralsib/ForeignExchangeTransactionTable.java index cf6f136f..c5fff964 100644 --- a/src/main/java/ru/investbook/parser/uralsib/ForeignExchangeTransactionTable.java +++ b/src/main/java/ru/investbook/parser/uralsib/ForeignExchangeTransactionTable.java @@ -20,6 +20,8 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.report_parser.api.ForeignExchangeTransaction; import org.spacious_team.table_wrapper.api.PatternTableColumn; import org.spacious_team.table_wrapper.api.TableColumn; @@ -29,22 +31,23 @@ import java.math.BigDecimal; +import static org.springframework.util.StringUtils.hasLength; import static ru.investbook.parser.uralsib.ForeignExchangeTransactionTable.FxTransactionTableHeader.*; @Slf4j public class ForeignExchangeTransactionTable extends SingleAbstractReportTable { private static final String TABLE_NAME = "Биржевые валютные сделки, совершенные в отчетном периоде"; private static final String CONTRACT_PREFIX = "Инструмент:"; - private String instrument = null; + private @MonotonicNonNull String instrument = null; public ForeignExchangeTransactionTable(UralsibBrokerReport report) { super(report, TABLE_NAME, "", FxTransactionTableHeader.class, 2); } @Override - protected ForeignExchangeTransaction parseRow(TableRow row) { + protected @Nullable ForeignExchangeTransaction parseRow(TableRow row) { String tradeId; - Object cellValue = row.getCellValue(TRADE_ID); + @Nullable Object cellValue = row.getCellValue(TRADE_ID); if (cellValue instanceof String) { String stringValue = cellValue.toString(); try { @@ -62,7 +65,7 @@ protected ForeignExchangeTransaction parseRow(TableRow row) { } else { return null; } - if (instrument == null || instrument.isEmpty()) { + if (!hasLength(instrument)) { return null; } diff --git a/src/main/java/ru/investbook/parser/uralsib/SecuritiesTable.java b/src/main/java/ru/investbook/parser/uralsib/SecuritiesTable.java index f4efa028..f4e8ccec 100644 --- a/src/main/java/ru/investbook/parser/uralsib/SecuritiesTable.java +++ b/src/main/java/ru/investbook/parser/uralsib/SecuritiesTable.java @@ -67,6 +67,7 @@ public Security getSecurityByCfi(String cfi) { .orElseThrow(() -> new RuntimeException("ЦБ с номером гос. регистрации/CFI не найден: " + cfi)); } + @Getter enum SecuritiesTableHeader implements TableHeaderColumn { NAME("наименование"), ISIN("isin"), @@ -77,7 +78,6 @@ enum SecuritiesTableHeader implements TableHeaderColumn { AMOUNT("Стоимость позиции по цене закрытия"), // в рублях для СПБ биржи ACCRUED_INTEREST("^нкд$"); // в валюте для валютных облигаций - @Getter private final TableColumn column; SecuritiesTableHeader(String... words) { @@ -89,7 +89,7 @@ enum SecuritiesTableHeader implements TableHeaderColumn { @ToString @Builder(toBuilder = true) @EqualsAndHashCode - static class ReportSecurityInformation { + public static class ReportSecurityInformation { private final Security security; private final String cfi; private final int incomingCount; diff --git a/src/main/java/ru/investbook/parser/uralsib/SecurityDepositAndWithdrawalTable.java b/src/main/java/ru/investbook/parser/uralsib/SecurityDepositAndWithdrawalTable.java index cba9cf14..ccdd8f7b 100644 --- a/src/main/java/ru/investbook/parser/uralsib/SecurityDepositAndWithdrawalTable.java +++ b/src/main/java/ru/investbook/parser/uralsib/SecurityDepositAndWithdrawalTable.java @@ -18,6 +18,7 @@ package ru.investbook.parser.uralsib; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.Security; import org.spacious_team.broker.report_parser.api.SecurityTransaction; import org.spacious_team.table_wrapper.api.TableRow; @@ -42,7 +43,7 @@ public SecurityDepositAndWithdrawalTable(UralsibBrokerReport report, SecuritiesT } @Override - protected SecurityTransaction parseRow(TableRow row) { + protected @Nullable SecurityTransaction parseRow(TableRow row) { String operation = row.getStringCellValue(OPERATION); if (!operation.equalsIgnoreCase(IN_DESCRIPTION) && !operation.equalsIgnoreCase(OUT_DESCRIPTION)) { return null; diff --git a/src/main/java/ru/investbook/parser/uralsib/SecurityTransactionTable.java b/src/main/java/ru/investbook/parser/uralsib/SecurityTransactionTable.java index 82d6c975..0bf7a6e8 100644 --- a/src/main/java/ru/investbook/parser/uralsib/SecurityTransactionTable.java +++ b/src/main/java/ru/investbook/parser/uralsib/SecurityTransactionTable.java @@ -20,6 +20,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.Security; import org.spacious_team.broker.report_parser.api.SecurityTransaction; import org.spacious_team.table_wrapper.api.AnyOfTableColumn; @@ -29,7 +30,6 @@ import org.spacious_team.table_wrapper.api.TableColumn; import org.spacious_team.table_wrapper.api.TableHeaderColumn; import org.spacious_team.table_wrapper.api.TableRow; -import org.checkerframework.checker.nullness.qual.Nullable; import ru.investbook.parser.SingleAbstractReportTable; import ru.investbook.parser.TransactionValueAndFeeParser; @@ -70,7 +70,7 @@ protected SecurityTransactionTable(UralsibBrokerReport report, @Nullable @Override protected SecurityTransaction parseRow(TableRow row) { - String tradeId = getTradeId(row, TRADE_ID); + @Nullable String tradeId = getTradeId(row, TRADE_ID); if (tradeId == null) { security = getSecurity(row); return null; @@ -115,8 +115,7 @@ protected SecurityTransaction parseRow(TableRow row) { .build(); } - @Nullable - static String getTradeId(TableRow row, TableHeaderColumn column) { + static @Nullable String getTradeId(TableRow row, TableHeaderColumn column) { try { // some numbers (doubles) represented by string type cells return String.valueOf(row.getLongCellValue(column)); @@ -125,8 +124,8 @@ static String getTradeId(TableRow row, TableHeaderColumn column) { } } - @Nullable - private Security getSecurity(TableRow row) { + private @Nullable Security getSecurity(TableRow row) { + //noinspection DataFlowIssue return Optional.ofNullable(row.getStringCellValueOrDefault(TRADE_ID, null)) .filter(securityDescription -> !securityDescription.startsWith("Итого")) .map(securityDescription -> securityDescription.split(" ")) @@ -135,6 +134,7 @@ private Security getSecurity(TableRow row) { .orElse(null); } + @Getter enum TransactionTableHeader implements TableHeaderColumn { DATE_TIME( AnyOfTableColumn.of( @@ -175,7 +175,6 @@ enum TransactionTableHeader implements TableHeaderColumn { PatternTableColumn.of("валюта списания")), VALUE_CURRENCY.getColumn())); // new report (fallback to value currency) - @Getter private final TableColumn column; TransactionTableHeader(String... words) { diff --git a/src/main/java/ru/investbook/parser/vtb/CashFlowEventTable.java b/src/main/java/ru/investbook/parser/vtb/CashFlowEventTable.java index 74d9000f..91ed5546 100644 --- a/src/main/java/ru/investbook/parser/vtb/CashFlowEventTable.java +++ b/src/main/java/ru/investbook/parser/vtb/CashFlowEventTable.java @@ -22,6 +22,7 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.broker.pojo.CashFlowType; import org.spacious_team.table_wrapper.api.PatternTableColumn; import org.spacious_team.table_wrapper.api.TableColumn; @@ -48,8 +49,8 @@ public CashFlowEventTable(SingleBrokerReport report) { } @Override - protected CashFlowEvent parseRow(TableRow row) { - String operation = row.getStringCellValueOrDefault(OPERATION, null); + protected @Nullable CashFlowEvent parseRow(TableRow row) { + @Nullable String operation = row.getStringCellValueOrDefault(OPERATION, null); if (operation == null) { return null; } @@ -85,7 +86,7 @@ public List getData() { @Slf4j @Builder @EqualsAndHashCode - static class CashFlowEvent { + public static class CashFlowEvent { private static final String duplicateOperation = "Перераспределение дохода между субсчетами / торговыми площадками"; private final Instant date; private final BigDecimal value;