diff --git a/.gitignore b/.gitignore
index 9f4f166a..a0272d19 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,7 @@ investbook/
!.idea/codeStyles
!.idea/copyright
!.idea/inspectionProfiles
+!.idea/encodings.xml
### NetBeans ###
/nbproject/private/
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 00000000..1d82e82f
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index d97c7421..ee9014b8 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
[](https://openjdk.org/)
-[](https://github.com/spring-projects/spring-boot/releases)
+[](https://github.com/spring-projects/spring-boot/releases)
[](https://hitsofcode.com/github/spacious-team/investbook/view?branch=develop)
-
-
+[](https://github.com/spacious-team/investbook/pulls?q=is%3Apr+is%3Aclosed)
+[](https://github.com/spacious-team/investbook/actions/workflows/publish-docker.yml)
[](https://github.com/spacious-team/investbook/releases/latest)
[](https://hub.docker.com/r/spaciousteam/investbook)
[](https://t.me/investbook_official)
diff --git a/docs/dbms-changing.md b/docs/dbms-changing.md
index 3f737a2f..111cb072 100644
--- a/docs/dbms-changing.md
+++ b/docs/dbms-changing.md
@@ -6,6 +6,7 @@
Если у вас недостаточно опыта, то рекомендуется пропустить этот раздел.
Возможен переход на [MariaDB](https://downloads.mariadb.org/).
+Поддерживаются версии, указанные в документации [Flyway](https://documentation.red-gate.com/fd/mariadb-184127600.html).
После установки в файле `application-conf.properties` необходимо прописать
```
spring.profiles.active=core,mariadb,conf
diff --git a/pom.xml b/pom.xml
index 4d7241e9..2748328d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,12 +23,12 @@
org.springframework.boot
spring-boot-starter-parent
- 3.0.6
+ 3.0.7
ru.investbook
investbook
- 2023.1
+ 2023.2
investbook
Investor Accounting Book
@@ -62,7 +62,7 @@
- 23.1
+ 23.2
20
@@ -427,7 +427,7 @@
- gcr.io/paketo-buildpacks/java:9.8.0
+ gcr.io/paketo-buildpacks/java:9.20.0
diff --git a/src/main/java/ru/investbook/parser/tinkoff/SecurityCodeAndIsinTable.java b/src/main/java/ru/investbook/parser/tinkoff/SecurityCodeAndIsinTable.java
index 291723b2..adaed583 100644
--- a/src/main/java/ru/investbook/parser/tinkoff/SecurityCodeAndIsinTable.java
+++ b/src/main/java/ru/investbook/parser/tinkoff/SecurityCodeAndIsinTable.java
@@ -20,20 +20,22 @@
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
+import lombok.RequiredArgsConstructor;
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;
+import org.spacious_team.table_wrapper.api.OptionalTableColumn;
import org.spacious_team.table_wrapper.api.PatternTableColumn;
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.util.StringUtils;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
+import static org.springframework.util.StringUtils.hasLength;
import static ru.investbook.parser.tinkoff.SecurityCodeAndIsinTable.SecurityAndCodeTableHeader.*;
import static ru.investbook.parser.tinkoff.TinkoffBrokerReport.tablesLastRowPattern;
@@ -54,8 +56,10 @@ protected SecurityCodeAndIsinTable(BrokerReport report) {
@Override
protected Void parseRow(TableRow row) {
String code = row.getStringCellValueOrDefault(CODE, null);
- if (StringUtils.hasLength(code) && !code.contains("Код актива")) { // exclude table's empty row
- codeToIsin.put(code, row.getStringCellValue(ISIN));
+ if (hasLength(code) && !code.contains("Код актива")) { // exclude table's empty row
+ // если колонка ISIN отсутствует, то ISIN используется в отчете вместо кода (SBERP)
+ String isin = row.getStringCellValueOrDefault(ISIN, code);
+ codeToIsin.put(code, isin);
String type = row.getStringCellValueOrDefault(TYPE, "").toLowerCase();
SecurityType securityType;
@@ -74,7 +78,7 @@ protected Void parseRow(TableRow row) {
}
String shortName = row.getStringCellValueOrDefault(SHORT_NAME, null);
- if (StringUtils.hasLength(shortName)) {
+ if (hasLength(shortName)) {
shortNameToCode.put(shortName, code);
}
}
@@ -105,10 +109,11 @@ public String getCode(String shortName) {
return Objects.requireNonNull(shortNameToCode.get(shortName), "Не найден код бумаги");
}
+ @RequiredArgsConstructor
protected enum SecurityAndCodeTableHeader implements TableHeaderColumn {
SHORT_NAME("Сокращенное", "наименование"),
- CODE("код", "актива"),
- ISIN("isin"),
+ CODE("код", "актива"), // код (SBERP) или ISIN
+ ISIN(optional("isin")), // может отсутствовать, если колонка CODE заполняется ISIN
TYPE("^Тип$"),
FACE_VALUE("Номинал");
@@ -118,5 +123,9 @@ protected enum SecurityAndCodeTableHeader implements TableHeaderColumn {
SecurityAndCodeTableHeader(String... words) {
this.column = PatternTableColumn.of(words);
}
+
+ static OptionalTableColumn optional(String... words) {
+ return OptionalTableColumn.of(PatternTableColumn.of(words));
+ }
}
}
diff --git a/src/main/java/ru/investbook/parser/tinkoff/TinkoffReportTables.java b/src/main/java/ru/investbook/parser/tinkoff/TinkoffReportTables.java
index e8a6b386..a99348d8 100644
--- a/src/main/java/ru/investbook/parser/tinkoff/TinkoffReportTables.java
+++ b/src/main/java/ru/investbook/parser/tinkoff/TinkoffReportTables.java
@@ -40,10 +40,12 @@ public class TinkoffReportTables extends AbstractReportTables getSecuritiesTable() {
@Override
public ReportTable getTransactionTable() {
ReportTable transactionTable =
- TinkoffSecurityTransactionTable.of(report, securityCodeAndIsinTable, transactionValueAndFeeParser);
+ TinkoffSecurityTransactionTable.of(report, securityCodeAndIsinTable, transactionValueAndFeeParser,
+ tinkoffSecurityTransactionTableHelper);
TinkoffDepositAndWithdrawalTable depositAndWithdrawalTable =
new TinkoffDepositAndWithdrawalTable(report, transactionTable, securityCodeAndIsinTable);
return WrappingReportTable.of(transactionTable, depositAndWithdrawalTable);
diff --git a/src/main/java/ru/investbook/parser/tinkoff/TinkoffReportTablesFactory.java b/src/main/java/ru/investbook/parser/tinkoff/TinkoffReportTablesFactory.java
index 3e462f13..8866d226 100644
--- a/src/main/java/ru/investbook/parser/tinkoff/TinkoffReportTablesFactory.java
+++ b/src/main/java/ru/investbook/parser/tinkoff/TinkoffReportTablesFactory.java
@@ -25,12 +25,14 @@
import org.springframework.stereotype.Component;
import ru.investbook.parser.TransactionValueAndFeeParser;
import ru.investbook.report.ForeignExchangeRateService;
+import ru.investbook.service.moex.MoexDerivativeCodeService;
@Component
@RequiredArgsConstructor
public class TinkoffReportTablesFactory implements ReportTablesFactory {
private final TransactionValueAndFeeParser transactionValueAndFeeParser;
private final ForeignExchangeRateService foreignExchangeRateService;
+ private final MoexDerivativeCodeService moexDerivativeCodeService;
@Override
public boolean canCreate(BrokerReport brokerReport) {
@@ -39,7 +41,9 @@ public boolean canCreate(BrokerReport brokerReport) {
@Override
public ReportTables create(BrokerReport brokerReport) {
+ TinkoffSecurityTransactionTableHelper tinkoffSecurityTransactionTableHelper =
+ new TinkoffSecurityTransactionTableHelper(moexDerivativeCodeService);
return new TinkoffReportTables((TinkoffBrokerReport) brokerReport,
- transactionValueAndFeeParser, foreignExchangeRateService);
+ transactionValueAndFeeParser, foreignExchangeRateService, tinkoffSecurityTransactionTableHelper);
}
}
diff --git a/src/main/java/ru/investbook/parser/tinkoff/TinkoffSecurityTransactionTable.java b/src/main/java/ru/investbook/parser/tinkoff/TinkoffSecurityTransactionTable.java
index 696be0b1..f2a0df27 100644
--- a/src/main/java/ru/investbook/parser/tinkoff/TinkoffSecurityTransactionTable.java
+++ b/src/main/java/ru/investbook/parser/tinkoff/TinkoffSecurityTransactionTable.java
@@ -21,6 +21,7 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+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;
@@ -39,37 +40,39 @@
import java.time.Instant;
import static ru.investbook.parser.tinkoff.TinkoffSecurityTransactionTable.TransactionTableHeader.*;
-import static ru.investbook.parser.tinkoff.TinkoffSecurityTransactionTableHelper.getSecurityId;
-import static ru.investbook.parser.tinkoff.TinkoffSecurityTransactionTableHelper.getSecurityType;
@Slf4j
public class TinkoffSecurityTransactionTable extends SingleAbstractReportTable {
private final SecurityCodeAndIsinTable codeAndIsin;
private final TransactionValueAndFeeParser transactionValueAndFeeParser;
+ private final TinkoffSecurityTransactionTableHelper transactionTableHelper;
public static ReportTable of(TinkoffBrokerReport report,
SecurityCodeAndIsinTable codeAndIsin,
- TransactionValueAndFeeParser transactionValueAndFeeParser) {
+ TransactionValueAndFeeParser transactionValueAndFeeParser,
+ TinkoffSecurityTransactionTableHelper transactionTableHelper) {
return WrappingReportTable.of(
new TinkoffSecurityTransactionTable(
"1.1 Информация о совершенных и исполненных сделках",
- report, codeAndIsin, transactionValueAndFeeParser),
+ report, codeAndIsin, transactionValueAndFeeParser, transactionTableHelper),
new TinkoffSecurityTransactionTable(
"1.2 Информация о неисполненных сделках на конец отчетного периода",
- report, codeAndIsin, transactionValueAndFeeParser));
+ report, codeAndIsin, transactionValueAndFeeParser, transactionTableHelper));
}
private TinkoffSecurityTransactionTable(String tableNamePrefix,
TinkoffBrokerReport report,
SecurityCodeAndIsinTable codeAndIsin,
- TransactionValueAndFeeParser transactionValueAndFeeParser) {
+ TransactionValueAndFeeParser transactionValueAndFeeParser,
+ TinkoffSecurityTransactionTableHelper transactionTableHelper) {
super(report,
cell -> cell.startsWith(tableNamePrefix),
cell -> TinkoffBrokerReport.tablesLastRowPattern.matcher(cell).lookingAt(),
TransactionTableHeader.class);
this.codeAndIsin = codeAndIsin;
this.transactionValueAndFeeParser = transactionValueAndFeeParser;
+ this.transactionTableHelper = transactionTableHelper;
}
@Override
@@ -78,7 +81,7 @@ protected AbstractTransaction parseRow(TableRow row) {
if (_tradeId == -1) return null;
String tradeId = String.valueOf(_tradeId);
- int securityId = getSecurityId(row, codeAndIsin, getReport().getSecurityRegistrar());
+ int securityId = transactionTableHelper.getSecurityId(row, codeAndIsin, getReport().getSecurityRegistrar());
boolean isBuy = row.getStringCellValue(OPERATION).toLowerCase().contains("покупка");
int count = Math.abs(row.getIntCellValue(COUNT));
BigDecimal amount = row.getBigDecimalCellValue(AMOUNT).abs();
@@ -86,7 +89,8 @@ protected AbstractTransaction parseRow(TableRow row) {
count = isBuy ? count : -count;
Instant timestamp = null;
- AbstractTransaction.AbstractTransactionBuilder, ?> builder = switch (getSecurityType(row)) {
+ SecurityType securityType = transactionTableHelper.getSecurityType(row);
+ AbstractTransaction.AbstractTransactionBuilder, ?> builder = switch (securityType) {
case STOCK -> SecurityTransaction.builder()
.timestamp(timestamp = getStockAndBondTransactionInstant(row));
case BOND, STOCK_OR_BOND -> {
@@ -137,7 +141,11 @@ protected AbstractTransaction parseRow(TableRow row) {
}
private Instant getStockAndBondTransactionInstant(TableRow row) {
- return getReport().convertToInstant(row.getStringCellValue(SETTLEMENT_DATE));
+ String settlementDate = row.getStringCellValue(SETTLEMENT_DATE);
+ // В старых отчетах одна дата, с января 2023 две: план/факт
+ String[] dates = settlementDate.split("/");
+ String date = (dates.length == 1) ? dates[0] : dates[1];
+ return getReport().convertToInstant(date);
}
private Instant getDerivativeAndCurrencyPairTransactionInstant(TableRow row) {
@@ -150,7 +158,7 @@ protected enum TransactionTableHeader implements TableHeaderColumn {
TRADE_ID("номер", "сделки"),
TRANSACTION_DATE("дата", "заклю", "чения"),
TRANSACTION_TIME("время"),
- TYPE("режим", "торгов"),
+ TYPE(optional("режим", "торгов")),
OPERATION("вид", "сделки"),
SHORT_NAME("сокращен", "наименова"),
CODE("код", "актива"),
@@ -177,5 +185,9 @@ protected enum TransactionTableHeader implements TableHeaderColumn {
TransactionTableHeader(String... words) {
this.column = PatternTableColumn.of(words);
}
+
+ static OptionalTableColumn optional(String... words) {
+ return OptionalTableColumn.of(PatternTableColumn.of(words));
+ }
}
}
diff --git a/src/main/java/ru/investbook/parser/tinkoff/TinkoffSecurityTransactionTableHelper.java b/src/main/java/ru/investbook/parser/tinkoff/TinkoffSecurityTransactionTableHelper.java
index 2063826b..7fd595ad 100644
--- a/src/main/java/ru/investbook/parser/tinkoff/TinkoffSecurityTransactionTableHelper.java
+++ b/src/main/java/ru/investbook/parser/tinkoff/TinkoffSecurityTransactionTableHelper.java
@@ -18,19 +18,25 @@
package ru.investbook.parser.tinkoff;
+import lombok.RequiredArgsConstructor;
import org.spacious_team.broker.pojo.Security;
import org.spacious_team.broker.pojo.SecurityType;
import org.spacious_team.table_wrapper.api.TableRow;
+import org.springframework.stereotype.Component;
import ru.investbook.parser.SecurityRegistrar;
+import ru.investbook.service.moex.MoexDerivativeCodeService;
import java.math.BigDecimal;
import java.util.Objects;
import static ru.investbook.parser.tinkoff.TinkoffSecurityTransactionTable.TransactionTableHeader.*;
-class TinkoffSecurityTransactionTableHelper {
+@Component
+@RequiredArgsConstructor
+public class TinkoffSecurityTransactionTableHelper {
+ private final MoexDerivativeCodeService moexDerivativeCodeService;
- static int getSecurityId(TableRow row,
+ int getSecurityId(TableRow row,
SecurityCodeAndIsinTable codeAndIsin,
SecurityRegistrar registrar) {
String code = row.getStringCellValue(CODE);
@@ -40,7 +46,7 @@ static int getSecurityId(TableRow row,
return declareSecurity(security, registrar);
}
- static SecurityType getSecurityType(TableRow row) {
+ SecurityType getSecurityType(TableRow row) {
BigDecimal accruedInterest = row.getBigDecimalCellValueOrDefault(ACCRUED_INTEREST, BigDecimal.ZERO);
if (accruedInterest.floatValue() > 1e-3) {
return SecurityType.BOND;
@@ -56,6 +62,8 @@ static SecurityType getSecurityType(TableRow row) {
String shortName = row.getStringCellValueOrDefault(SHORT_NAME, "");
if (shortName != null && shortName.length() == 10 && shortName.charAt(6) == '_') { // USDRUB_TOM
return SecurityType.CURRENCY_PAIR;
+ } else if (moexDerivativeCodeService.isFuturesCode(shortName) || moexDerivativeCodeService.isOption(shortName)) {
+ return SecurityType.DERIVATIVE;
}
return SecurityType.STOCK;
diff --git a/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java b/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java
index fd7471d6..64ca1642 100644
--- a/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java
+++ b/src/main/java/ru/investbook/service/moex/MoexDerivativeCodeService.java
@@ -38,9 +38,9 @@
*
* @see Specifications ticker codes for Futures and Options
*/
+@Slf4j
@Component
@RequiredArgsConstructor
-@Slf4j
public class MoexDerivativeCodeService {
private final Map codeToShortnames = Stream.of(new String[][]{
@@ -56,6 +56,8 @@ public class MoexDerivativeCodeService {
{"MA", "MMI"}, // Индекс МосБиржи металлов и добычи
{"FN", "FNI"}, // Индекс МосБиржи финансов
{"CS", "CNI"}, // Индекс МосБиржи потребительского сектора
+ {"RB", "RGBI"}, // Индекс RGBI
+
{"AF", "AFLT"}, // ПАО "Аэрофлот" (о.а.)
{"AL", "ALRS"}, // АК "АЛРОСА" (ПАО) (о.а.)
{"CH", "CHMF"}, // ПАО "Северсталь" (о.а.)
@@ -80,7 +82,7 @@ public class MoexDerivativeCodeService {
{"TF", "TRNS"}, // 0, 1 стоимости одной акции ПАО "Транснефть"
{"VB", "VTBR"}, // Банк ВТБ (ПАО) (о.а.)
{"MG", "MAGN"}, // ПАО "Магнитогорский металлургический комбинат" (о.а.)
- {"PL", "PLZL"}, // ПАО "Полюс" (о.а.)
+ {"PZ", "PLZL"}, // ПАО "Полюс" (о.а.)
{"YN", "YNDF"}, // Яндекс Н.В. (о.а.)
{"AK", "AFKS"}, // АФК Система (о.а.)
{"IR", "IRAO"}, // ПАО "Интер РАО ЕЭС" (о.а.)
@@ -102,6 +104,7 @@ public class MoexDerivativeCodeService {
{"BI", "BIDU"}, // АДР Байду Инк
{"BA", "BABA"}, // АДР Алибаба Груп Холдинг Лимитед
{"SF", "SPYF"}, // SPDR S&P 500 ETF Trust
+ {"NA", "NASD"}, // Invesco QQQ ETF Trust Unit Series 1
{"DM", "GDAI"}, // Daimler AG (о.а.)
{"DB", "GDBK"}, // Deutsche Bank AG (о.а.)
{"SM", "GSIE"}, // Siemens AG (о.а.)
@@ -112,27 +115,55 @@ public class MoexDerivativeCodeService {
{"O4", "OFZ4"}, // "четырехлетние" облигации федерального займа
{"O6", "OFZ6"}, // "шестилетние" облигации федерального займа
{"MP", "MOPR"}, // ставка MosPrime
+ {"PS", "POSI"}, // ПАО Группа Позитив
+ {"SX", "STOX"}, // iShares Core EURO STOXX 50 UCITS ETF EUR (Dist)
+ {"HS", "HANG"}, // Tracker Fund of Hong Kong ETF
+ {"DX", "DAX"}, // iShares Core DAX UCITS ETF (DE)
+ {"N2", "NIKK"}, // iShares Core Nikkei 225 ETF
+ {"IS", "ISKJ"}, // ПАО "ИСКЧ"
+ {"WU", "WUSH"}, // ПАО "ВУШ Холдинг"
+ {"MV", "MVID"}, // ПАО "М.видео"
+ {"CM", "CBOM"}, // ПАО "М.видео"
+ {"SZ", "SGZH"}, // ПАО "Сегежа Групп"
+ {"BE", "BELU"}, // ПАО "Белуга Групп"
+ {"FL", "FLOT"}, // ПАО "Совкомфлот"
+
{"RR", "RUON"}, // ставка RUONIA
{"MF", "1MFR"}, // ставка RUSFAR
{"DF", "1MDR"}, // Ставка RUSFARUSD
- {"AU", "AUDU"}, // курс австралийский доллар – доллар США
- {"CY", "CY"}, // курс китайский юань – российский рубль
- {"ED", "ED"}, // курс евро – доллар США
+
+ {"CY", "CY"}, // курс китайский юань – российский рубль (старый)
+ {"CR", "CNY"}, // курс китайский юань – российский рубль (актуальный)
{"Eu", "Eu"}, // курс евро – российский рубль
- {"GU", "GBPU"}, // курс фунт стерлингов – доллар США
{"Si", "Si"}, // курс доллар США – российский рубль
+ {"USDRUBF", "USDRUBF"}, // курс доллара США - российский рубль
+ {"EURRUBF", "EURRUBF"}, // курс евро - российский рубль
+ {"CNYRUBF", "CNYRUBF"}, // курс китайский юань – российский рубль
+ {"TY", "TRY"}, // курс турецкая лира – российский рубль
+ {"HK", "HKD"}, // курс гонконгский доллар – российский рубль
+ {"AE", "AED"}, // курс дирхам ОАЭ – российский рубль
+ {"I2", "INR"}, // курс индийская рупия – российский рубль
+ {"KZ", "KZT"}, // курс казахстанский тенге – российский рубль
+ {"AR", "AMD"}, // курс армянский драм – российский рубль
+ {"ED", "ED"}, // курс евро – доллар США
+ {"AU", "AUDU"}, // курс австралийский доллар – доллар США
+ {"GU", "GBPU"}, // курс фунт стерлингов – доллар США
{"CA", "UCAD"}, // курс доллар США – канадский доллар
{"CF", "UCHF"}, // курс доллар США – швейцарский франк
{"JP", "UJPY"}, // курс доллар США – японская йена
{"TR", "UTRY"}, // курс доллар США – турецкая лира
+ {"UC", "UCNY"}, // курс доллар США – китайский юань
{"IN", "UINR"}, // курс доллара США к индийской рупии
{"UU", "UUAH"}, // курс доллар США – украинская гривна
{"EG", "EGBP"}, // курс евро – фунт стерлингов
{"EC", "ECAD"}, // курс евро – канадский доллар
{"EJ", "EJPY"}, // курс евро – японская йена
+
{"BR", "BR"}, // нефть BRENT
{"CU", "CU"}, // медь
{"GD", "GOLD"}, // золото
+ {"GL", "GL"}, // Золото (в рублях)
+ {"GLDRUBF", "GLDRUBF"}, // золото
{"PD", "PLD"}, // палладий
{"PT", "PLT"}, // платина
{"SV", "SILV"}, // серебро
@@ -145,7 +176,8 @@ public class MoexDerivativeCodeService {
{"Nl", "Nl"}, // никель с чистотой 99,80% (минимум)
{"Zn", "Zn"}, // цинк
{"NG", "NG"}, // природный газ
- {"WH", "WH4"} // пшеница
+ {"WH", "WH4"}, // пшеница
+ {"W4", "WHEAT"} // Индекс пшеницы
}).collect(Collectors.toMap(a -> a[0], a -> a[1]));
private final Map shortnameToCodes = codeToShortnames.entrySet()
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 96d55783..8afeddea 100644
--- a/src/main/java/ru/investbook/web/forms/model/TransactionModel.java
+++ b/src/main/java/ru/investbook/web/forms/model/TransactionModel.java
@@ -89,7 +89,7 @@ public class TransactionModel {
private BigDecimal price;
@Nullable
- @Positive
+ @PositiveOrZero
private BigDecimal accruedInterest;
/**
diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties
index c5a6a3e8..c077630a 100644
--- a/src/main/resources/application-dev.properties
+++ b/src/main/resources/application-dev.properties
@@ -17,10 +17,11 @@
#
# MariaDB
-#spring.datasource.url = jdbc:mysql://localhost:3306/portfolio?createDatabaseIfNotExist=true&serverTimezone=Europe/Moscow
+#spring.datasource.url = jdbc:mariadb://localhost:3306/portfolio?createDatabaseIfNotExist=true&serverTimezone=Europe/Moscow
#spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MariaDBDialect
#spring.datasource.username = root
#spring.datasource.password = 123456
+#spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
# H2
spring.datasource.url=jdbc:h2:file:~/investbook2-test;mode=mysql;non_keywords=value
diff --git a/src/main/resources/application-mariadb.properties b/src/main/resources/application-mariadb.properties
index c3e10dd5..b5b6be7a 100644
--- a/src/main/resources/application-mariadb.properties
+++ b/src/main/resources/application-mariadb.properties
@@ -1,4 +1,5 @@
-spring.datasource.url=jdbc:mysql://localhost:3306/portfolio?createDatabaseIfNotExist=true&serverTimezone=Europe/Moscow
+spring.datasource.url=jdbc:mariadb://localhost:3306/portfolio?createDatabaseIfNotExist=true&serverTimezone=Europe/Moscow
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MariaDBDialect
spring.datasource.username=root
-spring.datasource.password=123456
\ No newline at end of file
+spring.datasource.password=123456
+spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
\ No newline at end of file
diff --git a/src/main/resources/db/migration/h2/V2023_2_0_0.sql b/src/main/resources/db/migration/h2/V2023_2_0_0.sql
new file mode 100644
index 00000000..d017157e
--- /dev/null
+++ b/src/main/resources/db/migration/h2/V2023_2_0_0.sql
@@ -0,0 +1,26 @@
+/*
+ * InvestBook
+ * 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
+ * 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 .
+ */
+
+ALTER TABLE `event_cash_flow`
+ ALTER COLUMN `value` DECIMAL(15,6) NOT NULL COMMENT 'Размер';
+
+ALTER TABLE `security_event_cash_flow`
+ ALTER COLUMN `value` DECIMAL(15,6) NOT NULL COMMENT 'Размер';
+
+ALTER TABLE `transaction_cash_flow`
+ ALTER COLUMN `value` DECIMAL(15,6) NOT NULL COMMENT 'Размер';
diff --git a/src/main/resources/db/migration/mysql/V2020_11_0_0.sql b/src/main/resources/db/migration/mariadb/V2020_11_0_0.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2020_11_0_0.sql
rename to src/main/resources/db/migration/mariadb/V2020_11_0_0.sql
diff --git a/src/main/resources/db/migration/mysql/V2020_13_0_0.sql b/src/main/resources/db/migration/mariadb/V2020_13_0_0.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2020_13_0_0.sql
rename to src/main/resources/db/migration/mariadb/V2020_13_0_0.sql
diff --git a/src/main/resources/db/migration/mysql/V2020_7_0_0.sql b/src/main/resources/db/migration/mariadb/V2020_7_0_0.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2020_7_0_0.sql
rename to src/main/resources/db/migration/mariadb/V2020_7_0_0.sql
diff --git a/src/main/resources/db/migration/mysql/V2021_11_0_0.sql b/src/main/resources/db/migration/mariadb/V2021_11_0_0.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2021_11_0_0.sql
rename to src/main/resources/db/migration/mariadb/V2021_11_0_0.sql
diff --git a/src/main/resources/db/migration/mysql/V2021_11_0_2.sql b/src/main/resources/db/migration/mariadb/V2021_11_0_2.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2021_11_0_2.sql
rename to src/main/resources/db/migration/mariadb/V2021_11_0_2.sql
diff --git a/src/main/resources/db/migration/mysql/V2021_11_0_4.sql b/src/main/resources/db/migration/mariadb/V2021_11_0_4.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2021_11_0_4.sql
rename to src/main/resources/db/migration/mariadb/V2021_11_0_4.sql
diff --git a/src/main/resources/db/migration/mysql/V2021_2_0_0.sql b/src/main/resources/db/migration/mariadb/V2021_2_0_0.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2021_2_0_0.sql
rename to src/main/resources/db/migration/mariadb/V2021_2_0_0.sql
diff --git a/src/main/resources/db/migration/mysql/V2021_2_1_0.sql b/src/main/resources/db/migration/mariadb/V2021_2_1_0.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2021_2_1_0.sql
rename to src/main/resources/db/migration/mariadb/V2021_2_1_0.sql
diff --git a/src/main/resources/db/migration/mysql/V2021_4_1_0.sql b/src/main/resources/db/migration/mariadb/V2021_4_1_0.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2021_4_1_0.sql
rename to src/main/resources/db/migration/mariadb/V2021_4_1_0.sql
diff --git a/src/main/resources/db/migration/mysql/V2021_5_0_0.sql b/src/main/resources/db/migration/mariadb/V2021_5_0_0.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2021_5_0_0.sql
rename to src/main/resources/db/migration/mariadb/V2021_5_0_0.sql
diff --git a/src/main/resources/db/migration/mysql/V2021_6_0_0.sql b/src/main/resources/db/migration/mariadb/V2021_6_0_0.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2021_6_0_0.sql
rename to src/main/resources/db/migration/mariadb/V2021_6_0_0.sql
diff --git a/src/main/resources/db/migration/mysql/V2021_9_0_0.sql b/src/main/resources/db/migration/mariadb/V2021_9_0_0.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2021_9_0_0.sql
rename to src/main/resources/db/migration/mariadb/V2021_9_0_0.sql
diff --git a/src/main/resources/db/migration/mysql/V2022_2_0_0.sql b/src/main/resources/db/migration/mariadb/V2022_2_0_0.sql
similarity index 100%
rename from src/main/resources/db/migration/mysql/V2022_2_0_0.sql
rename to src/main/resources/db/migration/mariadb/V2022_2_0_0.sql
diff --git a/src/main/resources/db/migration/mariadb/V2023_2_0_0.sql b/src/main/resources/db/migration/mariadb/V2023_2_0_0.sql
new file mode 100644
index 00000000..fd4e2977
--- /dev/null
+++ b/src/main/resources/db/migration/mariadb/V2023_2_0_0.sql
@@ -0,0 +1,26 @@
+/*
+ * InvestBook
+ * 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
+ * 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 .
+ */
+
+ALTER TABLE `event_cash_flow` CHANGE COLUMN `value`
+ `value` DECIMAL(15,6) NOT NULL COMMENT 'Размер' AFTER `type`;
+
+ALTER TABLE `security_event_cash_flow` CHANGE COLUMN `value`
+ `value` DECIMAL(15,6) NOT NULL COMMENT 'Размер' AFTER `type`;
+
+ALTER TABLE `transaction_cash_flow` CHANGE COLUMN `value`
+ `value` DECIMAL(15,6) NOT NULL COMMENT 'Размер' AFTER `type`;
diff --git a/src/main/resources/templates/events/edit-form.html b/src/main/resources/templates/events/edit-form.html
index a4c0a360..169c875d 100644
--- a/src/main/resources/templates/events/edit-form.html
+++ b/src/main/resources/templates/events/edit-form.html
@@ -100,7 +100,7 @@ Редактировать событие