From 6cc9758a5b60f7d68ea4d8cfd93c2175a6947c13 Mon Sep 17 00:00:00 2001
From: "nikitkagorkov@gmail.com" <gerosp>
Date: Wed, 18 Oct 2023 00:04:29 +0300
Subject: [PATCH 1/3] Added subscription detection algorithm Renamed enity to
 entity

---
 .../data/api/TransactionNetworkDataSource.kt  | 11 +--
 .../subscriptio/data/local/MainDatabase.kt    |  2 +-
 .../data/local/dao/SubscriptionDao.kt         |  3 +-
 .../data/local/dbmodel/TransactionDbModel.kt  |  4 +-
 .../repository/SubscriptionRepository.kt      |  3 +-
 .../subscriptio/data/mapper/TokenMapper.kt    |  2 +-
 .../data/mapper/TransactionMapper.kt          | 22 ++---
 .../repository/TransactionRepositoryImpl.kt   |  7 +-
 .../subscriptio/domain/actions/DefinerAlg.kt  | 64 +++++++++++++
 .../subscriptio/domain/actions/Period.kt      | 90 +++++++++++++++++++
 .../domain/actions/mergeTransaction.kt        |  8 +-
 .../domain/actions/networkUpdate.kt           | 58 ++++++++++++
 .../subscriptio/domain/enity/ResultType.kt    |  5 --
 .../domain/{enity => entity}/Result.kt        |  2 +-
 .../subscriptio/domain/entity/ResultType.kt   |  5 ++
 .../{enity => entity}/SubscriptionEntity.kt   |  9 +-
 .../domain/{enity => entity}/Token.kt         |  2 +-
 .../{enity => entity}/TransactionEntity.kt    |  2 +-
 .../repository/TransactionRepository.kt       |  4 +-
 .../domain/usecase/GetTransactionUseCase.kt   |  4 +-
 .../presentation/AddSubscriptionActivity.kt   |  5 +-
 .../EditSubscriptionDetailActivity.kt         |  7 +-
 .../EditSubscriptionDetailViewModel.kt        |  2 +-
 .../subscriptio/presentation/MainActivity.kt  | 23 +++--
 .../SubscriptionDashBoardActivity.kt          | 14 ++-
 .../SubscriptionDetailActivity.kt             |  3 +-
 .../presentation/VtbLoginActivity.kt          | 26 ++----
 app/src/main/res/values/strings.xml           |  1 +
 28 files changed, 298 insertions(+), 90 deletions(-)
 create mode 100644 app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/DefinerAlg.kt
 create mode 100644 app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/Period.kt
 create mode 100644 app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/networkUpdate.kt
 delete mode 100644 app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/ResultType.kt
 rename app/src/main/java/com/threehundredbugs/subscriptio/domain/{enity => entity}/Result.kt (87%)
 create mode 100644 app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/ResultType.kt
 rename app/src/main/java/com/threehundredbugs/subscriptio/domain/{enity => entity}/SubscriptionEntity.kt (78%)
 rename app/src/main/java/com/threehundredbugs/subscriptio/domain/{enity => entity}/Token.kt (83%)
 rename app/src/main/java/com/threehundredbugs/subscriptio/domain/{enity => entity}/TransactionEntity.kt (96%)

diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/data/api/TransactionNetworkDataSource.kt b/app/src/main/java/com/threehundredbugs/subscriptio/data/api/TransactionNetworkDataSource.kt
index f2aa244..917ca26 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/data/api/TransactionNetworkDataSource.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/data/api/TransactionNetworkDataSource.kt
@@ -1,18 +1,13 @@
 package com.threehundredbugs.subscriptio.data.api
 
-import android.util.Log
 import com.threehundredbugs.subscriptio.data.api.exceptions.IncorrectTokenException
 import com.threehundredbugs.subscriptio.data.api.exceptions.handleNetworkExceptions
-import com.threehundredbugs.subscriptio.data.api.model.DataField
-import com.threehundredbugs.subscriptio.data.api.model.LinksField
-import com.threehundredbugs.subscriptio.data.api.model.MetaField
-import com.threehundredbugs.subscriptio.data.api.model.RiskField
 import com.threehundredbugs.subscriptio.data.api.model.TransactionApi
-import com.threehundredbugs.subscriptio.domain.enity.Token
+import com.threehundredbugs.subscriptio.domain.entity.Token
 import com.threehundredbugs.subscriptio.data.api.retrofit.BankApi
 import com.threehundredbugs.subscriptio.data.mapper.mapTokenApiToToken
-import com.threehundredbugs.subscriptio.domain.enity.Result
-import com.threehundredbugs.subscriptio.domain.enity.ResultType
+import com.threehundredbugs.subscriptio.domain.entity.Result
+import com.threehundredbugs.subscriptio.domain.entity.ResultType
 
 class TransactionNetworkDataSource {
     //    val token =
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/data/local/MainDatabase.kt b/app/src/main/java/com/threehundredbugs/subscriptio/data/local/MainDatabase.kt
index 74a5f2a..bc5e9d0 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/data/local/MainDatabase.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/data/local/MainDatabase.kt
@@ -9,7 +9,7 @@ import com.threehundredbugs.subscriptio.data.local.dao.SubscriptionDao
 import com.threehundredbugs.subscriptio.data.local.dao.TransactionDao
 import com.threehundredbugs.subscriptio.data.local.dbmodel.SubscriptionDbModel
 import com.threehundredbugs.subscriptio.data.local.dbmodel.TransactionDbModel
-import com.threehundredbugs.subscriptio.domain.enity.SubscriptionEntity
+import com.threehundredbugs.subscriptio.domain.entity.SubscriptionEntity
 
 @Database(
     entities = [SubscriptionDbModel::class, SubscriptionEntity::class, TransactionDbModel::class],
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/data/local/dao/SubscriptionDao.kt b/app/src/main/java/com/threehundredbugs/subscriptio/data/local/dao/SubscriptionDao.kt
index 62d0299..08f4fb3 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/data/local/dao/SubscriptionDao.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/data/local/dao/SubscriptionDao.kt
@@ -6,8 +6,7 @@ import androidx.room.Insert
 import androidx.room.OnConflictStrategy
 import androidx.room.Query
 import androidx.room.Update
-import com.threehundredbugs.subscriptio.domain.enity.SubscriptionEntity
-import kotlinx.coroutines.flow.Flow
+import com.threehundredbugs.subscriptio.domain.entity.SubscriptionEntity
 
 @Dao
 interface SubscriptionDao {
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/data/local/dbmodel/TransactionDbModel.kt b/app/src/main/java/com/threehundredbugs/subscriptio/data/local/dbmodel/TransactionDbModel.kt
index 6db7c0d..7215d72 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/data/local/dbmodel/TransactionDbModel.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/data/local/dbmodel/TransactionDbModel.kt
@@ -5,8 +5,8 @@ import androidx.room.PrimaryKey
 import java.time.LocalDateTime
 
 @Entity(tableName = "Transactions")
-data class TransactionDbModel (
-    @PrimaryKey()
+data class TransactionDbModel(
+    @PrimaryKey(autoGenerate = true)
     val id: Int = 0,
     val accountId: String,
     val amount: Double,
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/data/local/repository/SubscriptionRepository.kt b/app/src/main/java/com/threehundredbugs/subscriptio/data/local/repository/SubscriptionRepository.kt
index b7e1458..a7f5b0c 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/data/local/repository/SubscriptionRepository.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/data/local/repository/SubscriptionRepository.kt
@@ -1,8 +1,7 @@
 package com.threehundredbugs.subscriptio.data.local.repository
 
 import com.threehundredbugs.subscriptio.data.local.dao.SubscriptionDao
-import com.threehundredbugs.subscriptio.domain.enity.SubscriptionEntity
-import kotlinx.coroutines.flow.Flow
+import com.threehundredbugs.subscriptio.domain.entity.SubscriptionEntity
 
 class SubscriptionRepository(private val subscriptionDao: SubscriptionDao) {
     fun getAllSubscriptions(): List<SubscriptionEntity> = subscriptionDao.getAllSubscriptions()
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/data/mapper/TokenMapper.kt b/app/src/main/java/com/threehundredbugs/subscriptio/data/mapper/TokenMapper.kt
index 50c59e3..76287bc 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/data/mapper/TokenMapper.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/data/mapper/TokenMapper.kt
@@ -1,7 +1,7 @@
 package com.threehundredbugs.subscriptio.data.mapper
 
 import com.threehundredbugs.subscriptio.data.api.model.TokenApi
-import com.threehundredbugs.subscriptio.domain.enity.Token
+import com.threehundredbugs.subscriptio.domain.entity.Token
 
 fun mapTokenApiToToken(tokenApi: TokenApi) =// mapTokenApiToTokenEntity ??
     Token(tokenApi.token, tokenApi.expiresIn+System.currentTimeMillis()) // TODO: If the function is not called immediately, expiration time is incorrect
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/data/mapper/TransactionMapper.kt b/app/src/main/java/com/threehundredbugs/subscriptio/data/mapper/TransactionMapper.kt
index d80dccd..c823b3d 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/data/mapper/TransactionMapper.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/data/mapper/TransactionMapper.kt
@@ -1,20 +1,16 @@
 package com.threehundredbugs.subscriptio.data.mapper
 
-import android.os.Build
-import androidx.annotation.RequiresApi
 import com.threehundredbugs.subscriptio.data.api.model.TransactionApi
 import com.threehundredbugs.subscriptio.data.local.dbmodel.TransactionDbModel
-import com.threehundredbugs.subscriptio.domain.enity.AmountEntityField
-import com.threehundredbugs.subscriptio.domain.enity.BalanceEntityField
-import com.threehundredbugs.subscriptio.domain.enity.BankTransactionCodeEntityField
-import com.threehundredbugs.subscriptio.domain.enity.DataEntityField
-import com.threehundredbugs.subscriptio.domain.enity.ProprietaryBankTransactionCodeEntityField
-import com.threehundredbugs.subscriptio.domain.enity.TransactionEntity
-import com.threehundredbugs.subscriptio.domain.enity.TransactionEntityField
-import java.sql.Date
+import com.threehundredbugs.subscriptio.domain.entity.AmountEntityField
+import com.threehundredbugs.subscriptio.domain.entity.BalanceEntityField
+import com.threehundredbugs.subscriptio.domain.entity.BankTransactionCodeEntityField
+import com.threehundredbugs.subscriptio.domain.entity.DataEntityField
+import com.threehundredbugs.subscriptio.domain.entity.ProprietaryBankTransactionCodeEntityField
+import com.threehundredbugs.subscriptio.domain.entity.TransactionEntity
+import com.threehundredbugs.subscriptio.domain.entity.TransactionEntityField
 import java.text.SimpleDateFormat
-import java.time.LocalDateTime
-import java.time.format.DateTimeFormatter
+
 fun mapTransactionalApiToEntity(transactionApi: TransactionApi) = TransactionEntity(
     data = DataEntityField(
         transaction = transactionApi.data.transaction.map {
@@ -55,7 +51,7 @@ fun mapTransactionalApiToEntity(transactionApi: TransactionApi) = TransactionEnt
                     )
                 },
                 merchantDetails = it.merchantDetails?.let { merchantDetails ->
-                    com.threehundredbugs.subscriptio.domain.enity.MerchantDetailsEntityField(
+                    com.threehundredbugs.subscriptio.domain.entity.MerchantDetailsEntityField(
                         merchantName = merchantDetails.merchantName,
                         merchantCategoryCode = merchantDetails.merchantCategoryCode
                     )
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/data/repository/TransactionRepositoryImpl.kt b/app/src/main/java/com/threehundredbugs/subscriptio/data/repository/TransactionRepositoryImpl.kt
index 2632ca9..bd7dd84 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/data/repository/TransactionRepositoryImpl.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/data/repository/TransactionRepositoryImpl.kt
@@ -1,11 +1,10 @@
 package com.threehundredbugs.subscriptio.data.repository
 
 import com.threehundredbugs.subscriptio.data.api.TransactionNetworkDataSource
-import com.threehundredbugs.subscriptio.data.local.MainDatabase
 import com.threehundredbugs.subscriptio.data.mapper.mapTransactionalApiToEntity
-import com.threehundredbugs.subscriptio.domain.enity.Result
-import com.threehundredbugs.subscriptio.domain.enity.ResultType
-import com.threehundredbugs.subscriptio.domain.enity.TransactionEntity
+import com.threehundredbugs.subscriptio.domain.entity.Result
+import com.threehundredbugs.subscriptio.domain.entity.ResultType
+import com.threehundredbugs.subscriptio.domain.entity.TransactionEntity
 import com.threehundredbugs.subscriptio.domain.repository.TransactionRepository
 
 class TransactionRepositoryImpl: TransactionRepository {
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/DefinerAlg.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/DefinerAlg.kt
new file mode 100644
index 0000000..2113f5c
--- /dev/null
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/DefinerAlg.kt
@@ -0,0 +1,64 @@
+package com.threehundredbugs.subscriptio.domain.actions
+
+import android.icu.text.SimpleDateFormat
+import com.threehundredbugs.subscriptio.data.local.dbmodel.TransactionDbModel
+import com.threehundredbugs.subscriptio.domain.entity.SubscriptionEntity
+
+val SIMILAR_TRANSACTIONS_THRESHOLD = 2
+fun getSubscriptions(
+    transactions: MutableList<TransactionDbModel>
+
+): MutableList<SubscriptionEntity> {
+
+    val sortedByTimeTransactions = transactions.sortedBy { it.bookingDateTime }.toMutableList()
+    val subBuilder: MutableList<SubscriptionEntity> = mutableListOf()
+    while (sortedByTimeTransactions.isNotEmpty()) {
+        val latestTransaction = sortedByTimeTransactions[0]
+        sortedByTimeTransactions.removeAt(0)
+        for (transaction in sortedByTimeTransactions) {
+            for (prd in Period.values()) {
+                if (nodeOfChainSubs(latestTransaction, transaction, prd)) {
+                    if (prd.lst.isEmpty()) prd.lst.add(latestTransaction)
+                    prd.lst.add(transaction)
+                }
+            }
+        }
+        for (prd in Period.values()) {
+            if (prd.lst.size >= SIMILAR_TRANSACTIONS_THRESHOLD) {
+                subBuilder.add(
+                    SubscriptionEntity(
+                        active = prd.isActive(latestTransaction.bookingDateTime),
+                        name = latestTransaction.merchantName
+                            ?: "Неизвестная подписка", // TODO: strings.xml : unknown_subscription_label
+                        lastOrNextTransactionDate = SimpleDateFormat("yyyy.MM.dd HH:mm").format(
+                            latestTransaction.bookingDateTime
+                        ),
+                        sum = latestTransaction.amount.toString(),
+                        transactionDate = prd.lst.toMutableList(),
+                        bankCard = latestTransaction.accountId,
+                        subscriptionLink = "",
+                        period = prd.ordinal
+                    )
+                )
+                sortedByTimeTransactions.removeAll(prd.lst)
+            }
+            prd.lst.clear()
+        }
+
+    }
+    return subBuilder
+}
+
+
+fun nodeOfChainSubs(
+    first: TransactionDbModel,
+    second: TransactionDbModel,
+    period: Period
+): Boolean {
+    return first.currency == second.currency
+            &&
+            first.amount == second.amount &&
+            first.merchantName == second.merchantName &&
+            period.differ(first.bookingDateTime, second.bookingDateTime)
+}
+
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/Period.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/Period.kt
new file mode 100644
index 0000000..4781ed3
--- /dev/null
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/Period.kt
@@ -0,0 +1,90 @@
+package com.threehundredbugs.subscriptio.domain.actions
+
+import android.icu.util.Calendar
+import com.threehundredbugs.subscriptio.data.local.dbmodel.TransactionDbModel
+import kotlin.math.abs
+
+enum class Period {
+    WEEK {
+        override fun differ(firstDate: Long, secondDate: Long) =
+            kotlin.math.abs(
+                kotlin.math.abs(
+                    getDateInDays(firstDate / 1000) - getDateInDays(
+                        secondDate / 1000
+                    )
+                ) - 7L
+            ) <= 2
+
+        override fun isActive(last: Long): Boolean =
+            abs(Calendar.getInstance().timeInMillis - last) <= 7
+
+        override val lst: MutableList<TransactionDbModel> = mutableListOf()
+
+    },
+    TWO_WEEKS {
+        override fun differ(firstDate: Long, secondDate: Long) =
+            kotlin.math.abs(
+                kotlin.math.abs(
+                    getDateInDays(firstDate / 1000) - getDateInDays(
+                        secondDate / 1000
+                    )
+                ) - 14L
+            ) <= 4
+
+        override fun isActive(last: Long): Boolean =
+            abs(Calendar.getInstance().timeInMillis - last) <= 14
+
+        override val lst: MutableList<TransactionDbModel> = mutableListOf()
+    },
+    MONTH {
+        override fun differ(firstDate: Long, secondDate: Long) =
+            kotlin.math.abs(
+                kotlin.math.abs(
+                    getDateInDays(firstDate / 1000) - getDateInDays(
+                        secondDate / 1000
+                    )
+                ) - 31L
+            ) <= 5
+
+        override fun isActive(last: Long): Boolean =
+            abs(Calendar.getInstance().timeInMillis - last) <= 31
+
+        override val lst: MutableList<TransactionDbModel> = mutableListOf()
+    },
+    SIX_MONTHS {
+        override fun differ(firstDate: Long, secondDate: Long) =
+            kotlin.math.abs(
+                kotlin.math.abs(
+                    getDateInDays(firstDate / 1000) - getDateInDays(
+                        secondDate / 1000
+                    )
+                ) - 186L
+            ) <= 10
+
+        override fun isActive(last: Long): Boolean =
+            abs(Calendar.getInstance().timeInMillis - last) <= 186
+
+        override val lst: MutableList<TransactionDbModel> = mutableListOf()
+    },
+    YEAR {
+        override fun differ(firstDate: Long, secondDate: Long) =
+            kotlin.math.abs(
+                kotlin.math.abs(
+                    getDateInDays(firstDate / 1000) - getDateInDays(
+                        secondDate / 1000
+                    )
+                ) - 365L
+            ) <= 20
+
+        override fun isActive(last: Long): Boolean =
+            abs(Calendar.getInstance().timeInMillis - last) <= 365
+
+        override val lst: MutableList<TransactionDbModel> = mutableListOf()
+    };
+
+    abstract fun differ(firstDate: Long, secondDate: Long): Boolean
+    abstract fun isActive(last: Long): Boolean
+    abstract val lst: MutableList<TransactionDbModel>
+}
+
+fun getDateInDays(transactionDate: Long) = transactionDate / 86400
\ No newline at end of file
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/mergeTransaction.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/mergeTransaction.kt
index 4e89c4d..d906713 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/mergeTransaction.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/mergeTransaction.kt
@@ -1,8 +1,8 @@
 package com.threehundredbugs.subscriptio.domain.actions
 
-import com.threehundredbugs.subscriptio.domain.enity.Result
-import com.threehundredbugs.subscriptio.domain.enity.ResultType
-import com.threehundredbugs.subscriptio.domain.enity.TransactionEntity
+import com.threehundredbugs.subscriptio.domain.entity.Result
+import com.threehundredbugs.subscriptio.domain.entity.ResultType
+import com.threehundredbugs.subscriptio.domain.entity.TransactionEntity
 import com.threehundredbugs.subscriptio.data.local.repository.TransactionRepository
 import com.threehundredbugs.subscriptio.data.mapper.mapTransactionEntityToDbModel
 
@@ -16,7 +16,7 @@ suspend fun mergeTransactions(dbInstance: TransactionRepository, networkResult:
         val transactionModel = mapTransactionEntityToDbModel(transaction)
         val similarTransactions = dbInstance.getAllByDate(transactionModel.bookingDateTime, transactionModel.bookingDateTime)
         for (similarTransaction in similarTransactions) {
-            if (similarTransaction.equalWithoutId(transactionModel)) {
+            if (similarTransaction.equalWithoutId(transactionModel)) { // TODO: Add transactionId field to the entity so we can compare it if it is present
                 return
             }
         }
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/networkUpdate.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/networkUpdate.kt
new file mode 100644
index 0000000..c9dd307
--- /dev/null
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/networkUpdate.kt
@@ -0,0 +1,58 @@
+package com.threehundredbugs.subscriptio.domain.actions
+
+import android.util.Log
+import com.threehundredbugs.subscriptio.data.local.repository.SubscriptionRepository
+import com.threehundredbugs.subscriptio.data.local.repository.TransactionRepository
+import com.threehundredbugs.subscriptio.data.repository.TransactionRepositoryImpl
+import com.threehundredbugs.subscriptio.domain.entity.ResultType
+import com.threehundredbugs.subscriptio.domain.usecase.GetTransactionUseCase
+
+
+suspend fun networkUpdate(
+    transactionRepository: TransactionRepository,
+    subscriptionRepository: SubscriptionRepository
+) {
+    val transactionUseCase = GetTransactionUseCase(TransactionRepositoryImpl())
+
+    // Get updated transactions from the API
+    val networkResult = transactionUseCase.execute()
+    //TODO добавить реализацию получения данных из мокапа
+    if (networkResult.resultType == ResultType.ERROR) {
+        //TODO обработать ошибки
+        Log.e("NETWORK_ERROR", networkResult.error?.stackTraceToString() ?: "")
+    } else {
+        mergeTransactions(transactionRepository, networkResult) // Merge with the database
+    }
+
+    // Detect all available subscriptions
+    val subscriptions = getSubscriptions(
+        transactionRepository.getAll().toMutableList()
+    )
+    // Update existing subscriptions and add new ones
+    for (subscription in subscriptions) {
+        if (subscription.transactionDate.isEmpty()) continue
+        val subscriptionId =
+            subscriptionRepository.getAllSubscriptions().find {
+                it.sum == subscription.sum && it.lastOrNextTransactionDate == subscription.lastOrNextTransactionDate
+                        && it.bankCard == subscription.bankCard && it.period == subscription.period
+            }?.id
+        if (subscriptionId != null) { // if similar subscription was fund
+
+            for (transaction in subscription.transactionDate) // update it`s transactions
+                transactionRepository.update(transaction.copy(associatedSubscriptionId = subscriptionId))
+
+        } else { // if this is a new subscription
+            subscriptionRepository.insertSubscription(subscription)
+        }
+    }
+// Testing data
+//    val transactions = transactionRepository.getAll() + TransactionDbModel(
+//        id = 0,
+//        accountId = "87659",
+//        amount = 1000.0,
+//        currency = "RUB",
+//        bookingDateTime = 1548142387000,
+//        bankTransactionCode = "ReceivedCreditTransfer",
+//        bankTransactionSubCode = "DomesticCreditTransfer"
+//    )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/ResultType.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/ResultType.kt
deleted file mode 100644
index 3c1f9a2..0000000
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/ResultType.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.threehundredbugs.subscriptio.domain.enity
-
-enum class ResultType {
-    ERROR, SUCCESS
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/Result.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/Result.kt
similarity index 87%
rename from app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/Result.kt
rename to app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/Result.kt
index a903b88..57ec970 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/Result.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/Result.kt
@@ -1,4 +1,4 @@
-package com.threehundredbugs.subscriptio.domain.enity
+package com.threehundredbugs.subscriptio.domain.entity
 
 data class Result<out T>(
     var resultType: ResultType,
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/ResultType.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/ResultType.kt
new file mode 100644
index 0000000..039580c
--- /dev/null
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/ResultType.kt
@@ -0,0 +1,5 @@
+package com.threehundredbugs.subscriptio.domain.entity
+
+enum class ResultType {
+    ERROR, SUCCESS
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/SubscriptionEntity.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/SubscriptionEntity.kt
similarity index 78%
rename from app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/SubscriptionEntity.kt
rename to app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/SubscriptionEntity.kt
index 50aff17..72302d6 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/SubscriptionEntity.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/SubscriptionEntity.kt
@@ -1,8 +1,9 @@
-package com.threehundredbugs.subscriptio.domain.enity
+package com.threehundredbugs.subscriptio.domain.entity
 
 import androidx.room.Entity
 import androidx.room.Ignore
 import androidx.room.PrimaryKey
+import com.threehundredbugs.subscriptio.data.local.dbmodel.TransactionDbModel
 import java.io.Serializable
 
 @Entity(tableName = "Subscriptions")
@@ -13,7 +14,7 @@ data class SubscriptionEntity(
     val lastOrNextTransactionDate: String,
     val sum: String,
     @Ignore
-    val transactionDate: List<String>,
+    val transactionDate: List<TransactionDbModel>,
     val bankCard: String,
     val subscriptionLink: String,
     @PrimaryKey(autoGenerate = true)
@@ -44,4 +45,8 @@ data class SubscriptionEntity(
         period,
         notify
     )
+
+    override fun toString(): String {
+        return "Subscription $name for $sum"
+    }
 }
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/Token.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/Token.kt
similarity index 83%
rename from app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/Token.kt
rename to app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/Token.kt
index 1a13b0e..bbf747d 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/Token.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/Token.kt
@@ -1,4 +1,4 @@
-package com.threehundredbugs.subscriptio.domain.enity
+package com.threehundredbugs.subscriptio.domain.entity
 
 
 class Token(val token: String, val expiration: Long) {
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/TransactionEntity.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/TransactionEntity.kt
similarity index 96%
rename from app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/TransactionEntity.kt
rename to app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/TransactionEntity.kt
index e72f815..2270422 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/enity/TransactionEntity.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/TransactionEntity.kt
@@ -1,4 +1,4 @@
-package com.threehundredbugs.subscriptio.domain.enity
+package com.threehundredbugs.subscriptio.domain.entity
 
 data class TransactionEntity(
     val data: DataEntityField
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/repository/TransactionRepository.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/repository/TransactionRepository.kt
index 3cdfe36..a0953d8 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/repository/TransactionRepository.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/repository/TransactionRepository.kt
@@ -1,7 +1,7 @@
 package com.threehundredbugs.subscriptio.domain.repository
 
-import com.threehundredbugs.subscriptio.domain.enity.Result
-import com.threehundredbugs.subscriptio.domain.enity.TransactionEntity
+import com.threehundredbugs.subscriptio.domain.entity.Result
+import com.threehundredbugs.subscriptio.domain.entity.TransactionEntity
 
 interface TransactionRepository {
 
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/usecase/GetTransactionUseCase.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/usecase/GetTransactionUseCase.kt
index 3a9c66d..97851bf 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/usecase/GetTransactionUseCase.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/usecase/GetTransactionUseCase.kt
@@ -1,6 +1,6 @@
 package com.threehundredbugs.subscriptio.domain.usecase
-import com.threehundredbugs.subscriptio.domain.enity.Result
-import com.threehundredbugs.subscriptio.domain.enity.TransactionEntity
+import com.threehundredbugs.subscriptio.domain.entity.Result
+import com.threehundredbugs.subscriptio.domain.entity.TransactionEntity
 import com.threehundredbugs.subscriptio.domain.repository.TransactionRepository
 
 class GetTransactionUseCase(private val transactionRepository: TransactionRepository) {
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/AddSubscriptionActivity.kt b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/AddSubscriptionActivity.kt
index 42c3043..bd7d2ea 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/AddSubscriptionActivity.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/AddSubscriptionActivity.kt
@@ -3,7 +3,6 @@ package com.threehundredbugs.subscriptio.presentation
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
-import android.util.Log
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
 import androidx.compose.foundation.background
@@ -41,8 +40,8 @@ import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import com.threehundredbugs.subscriptio.R
 import com.threehundredbugs.subscriptio.data.repository.TransactionRepositoryImpl
-import com.threehundredbugs.subscriptio.domain.enity.TransactionEntity
-import com.threehundredbugs.subscriptio.domain.enity.TransactionEntityField
+import com.threehundredbugs.subscriptio.domain.entity.TransactionEntity
+import com.threehundredbugs.subscriptio.domain.entity.TransactionEntityField
 import com.threehundredbugs.subscriptio.domain.usecase.GetTransactionUseCase
 import com.threehundredbugs.subscriptio.presentation.ui.theme.SubScriptioTheme
 import kotlinx.coroutines.Dispatchers
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/EditSubscriptionDetailActivity.kt b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/EditSubscriptionDetailActivity.kt
index c7862b7..6021b7c 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/EditSubscriptionDetailActivity.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/EditSubscriptionDetailActivity.kt
@@ -5,7 +5,6 @@ import android.content.Intent
 import android.os.Bundle
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
-import androidx.activity.viewModels
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
@@ -48,7 +47,7 @@ import androidx.lifecycle.lifecycleScope
 import com.threehundredbugs.subscriptio.R
 import com.threehundredbugs.subscriptio.data.local.MainDatabase
 import com.threehundredbugs.subscriptio.data.local.repository.SubscriptionRepository
-import com.threehundredbugs.subscriptio.domain.enity.SubscriptionEntity
+import com.threehundredbugs.subscriptio.domain.entity.SubscriptionEntity
 import com.threehundredbugs.subscriptio.presentation.ui.theme.SubScriptioTheme
 import kotlinx.coroutines.launch
 
@@ -310,7 +309,7 @@ fun EditTransactionInterval(
 fun EditSubscriptionCost(subscription: SubscriptionEntity, onValueChange: (String) -> Unit) {
     var cost by remember {
         mutableStateOf(
-            subscription.sum.toIntOrNull() ?: 0
+            subscription.sum.toDoubleOrNull() ?: 0.0
         )
     }
     var isError by remember { mutableStateOf(false) }
@@ -327,7 +326,7 @@ fun EditSubscriptionCost(subscription: SubscriptionEntity, onValueChange: (Strin
             isError = isError,
             onValueChange = {
                 try {
-                    cost = it.toInt()
+                    cost = it.toDouble()
                     isError = false
                     onValueChange(it)
                 } catch (e: Exception) {
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/EditSubscriptionDetailViewModel.kt b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/EditSubscriptionDetailViewModel.kt
index 9171817..65cad16 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/EditSubscriptionDetailViewModel.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/EditSubscriptionDetailViewModel.kt
@@ -1,7 +1,7 @@
 package com.threehundredbugs.subscriptio.presentation
 
 import androidx.lifecycle.ViewModel
-import com.threehundredbugs.subscriptio.domain.enity.SubscriptionEntity
+import com.threehundredbugs.subscriptio.domain.entity.SubscriptionEntity
 
 class EditSubscriptionDetailViewModel(
     private val subscriptionEntity: SubscriptionEntity
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/MainActivity.kt b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/MainActivity.kt
index 007085b..1c0a763 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/MainActivity.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/MainActivity.kt
@@ -1,11 +1,9 @@
 package com.threehundredbugs.subscriptio.presentation
 
-import android.os.Build
 import android.os.Bundle
 import android.util.Log
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
-import androidx.annotation.RequiresApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
@@ -26,8 +24,6 @@ import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.lifecycle.lifecycleScope
 import com.threehundredbugs.subscriptio.R
-import com.threehundredbugs.subscriptio.data.api.TransactionNetworkDataSource
-import com.threehundredbugs.subscriptio.data.api.retrofit.BankApi
 import com.threehundredbugs.subscriptio.data.cache.CacheUtils
 import com.threehundredbugs.subscriptio.data.cache.CacheUtils.IS_USER_LOGIN
 import com.threehundredbugs.subscriptio.data.local.MainDatabase
@@ -35,10 +31,8 @@ import com.threehundredbugs.subscriptio.data.local.repository.SubscriptionReposi
 import com.threehundredbugs.subscriptio.data.local.repository.TransactionRepository
 import com.threehundredbugs.subscriptio.data.repository.TransactionRepositoryImpl
 
-import com.threehundredbugs.subscriptio.domain.enity.SubscriptionEntity
-
 import com.threehundredbugs.subscriptio.domain.actions.mergeTransactions
-import com.threehundredbugs.subscriptio.domain.enity.ResultType
+import com.threehundredbugs.subscriptio.domain.entity.ResultType
 
 import com.threehundredbugs.subscriptio.domain.usecase.GetTransactionUseCase
 import com.threehundredbugs.subscriptio.presentation.theme.SubScriptioTheme
@@ -65,6 +59,21 @@ class MainActivity : ComponentActivity() {
             TransactionRepository(MainDatabase.getInstance(application).transactionDao())
         }
 
+
+//        lifecycleScope.launch {
+//            Log.d("UPDATE","Network update started")
+//
+//            networkUpdate(transactionRepository, subscriptionRepository)
+//            Log.d("UPDATE","Network update finished")
+//        }
+//        runBlocking{
+//            Log.d("UPDATE","Network update started")
+//
+//            networkUpdate(transactionRepository, subscriptionRepository)
+//            Log.d("UPDATE","Network update finished")
+//        }
+
+
         //Log.d("ROOM_LOG", transactionRepository.getAll().toString())
 
 
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/SubscriptionDashBoardActivity.kt b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/SubscriptionDashBoardActivity.kt
index 26c3d1a..dd33dd9 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/SubscriptionDashBoardActivity.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/SubscriptionDashBoardActivity.kt
@@ -55,11 +55,11 @@ import androidx.lifecycle.lifecycleScope
 import com.threehundredbugs.subscriptio.R
 import com.threehundredbugs.subscriptio.data.local.MainDatabase
 import com.threehundredbugs.subscriptio.data.local.repository.SubscriptionRepository
-import com.threehundredbugs.subscriptio.domain.enity.SubscriptionEntity
+import com.threehundredbugs.subscriptio.data.local.repository.TransactionRepository
+import com.threehundredbugs.subscriptio.domain.actions.networkUpdate
+import com.threehundredbugs.subscriptio.domain.entity.SubscriptionEntity
 import com.threehundredbugs.subscriptio.presentation.ui.theme.SubScriptioTheme
-import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
 
 class SubscriptionDashBoardActivity : ComponentActivity() {
 
@@ -67,10 +67,18 @@ class SubscriptionDashBoardActivity : ComponentActivity() {
         SubscriptionRepository(MainDatabase.getInstance(application).subscriptionDao())
     }
 
+    private val transactionRepository by lazy {
+        TransactionRepository(MainDatabase.getInstance(application).transactionDao())
+    }
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+        lifecycleScope.launch {
+            Log.d("UPDATE", "Network update started")
 
+            networkUpdate(transactionRepository, subscriptionRepository)
+            Log.d("UPDATE", "Network update finished")
+        }
         setContent {
 
             var subscription by remember {
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/SubscriptionDetailActivity.kt b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/SubscriptionDetailActivity.kt
index 18198d2..c43da97 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/SubscriptionDetailActivity.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/SubscriptionDetailActivity.kt
@@ -26,14 +26,13 @@ import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import com.threehundredbugs.subscriptio.R
-import com.threehundredbugs.subscriptio.domain.enity.SubscriptionEntity
+import com.threehundredbugs.subscriptio.domain.entity.SubscriptionEntity
 import com.threehundredbugs.subscriptio.presentation.ui.theme.SubScriptioTheme
 
 class SubscriptionDetailActivity : ComponentActivity() {
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/VtbLoginActivity.kt b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/VtbLoginActivity.kt
index d7e319b..512b2fb 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/VtbLoginActivity.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/VtbLoginActivity.kt
@@ -3,7 +3,6 @@ package com.threehundredbugs.subscriptio.presentation
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
-import android.util.Log
 import android.view.ViewGroup
 import android.webkit.WebView
 import android.webkit.WebViewClient
@@ -20,22 +19,16 @@ import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.viewinterop.AndroidView
-import androidx.lifecycle.lifecycleScope
 import com.threehundredbugs.subscriptio.R
 import com.threehundredbugs.subscriptio.data.cache.CacheUtils
 import com.threehundredbugs.subscriptio.data.cache.CacheUtils.IS_USER_LOGIN
 import com.threehundredbugs.subscriptio.data.local.MainDatabase
 import com.threehundredbugs.subscriptio.data.local.repository.TransactionRepository
 import com.threehundredbugs.subscriptio.data.repository.TransactionRepositoryImpl
-import com.threehundredbugs.subscriptio.domain.actions.mergeTransactions
-import com.threehundredbugs.subscriptio.domain.enity.ResultType
 import com.threehundredbugs.subscriptio.domain.usecase.GetTransactionUseCase
 import com.threehundredbugs.subscriptio.presentation.ui.theme.SubScriptioTheme
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
 
 class VtbLoginActivity : ComponentActivity() {
     private val transactionUseCase = GetTransactionUseCase(TransactionRepositoryImpl())
@@ -57,18 +50,11 @@ class VtbLoginActivity : ComponentActivity() {
                             modifier = Modifier
                                 .fillMaxHeight(0.05f)
                                 .clickable {
-                                    CacheUtils.setBoolean(IS_USER_LOGIN, true, this@VtbLoginActivity)
-                                    lifecycleScope.launch(Dispatchers.IO) {
-                                        val networkResult = transactionUseCase.execute()
-                                        //TODO добавить реализацию получения данных из мокапа
-                                        if (networkResult.resultType == ResultType.ERROR) {
-                                            //TODO обработать ошибки
-                                            Log.e("NETWORK_ERROR", networkResult.error?.stackTraceToString() ?: "")
-                                        }
-                                        else {
-                                            mergeTransactions(transactionRepository, networkResult)
-                                        }
-                                    }
+                                    CacheUtils.setBoolean(
+                                        IS_USER_LOGIN,
+                                        true,
+                                        this@VtbLoginActivity
+                                    )
                                     startActivity(SubscriptionDashBoardActivity.newInstance(this@VtbLoginActivity))
                                     finish()
                                 },
@@ -82,10 +68,12 @@ class VtbLoginActivity : ComponentActivity() {
             }
         }
     }
+
     companion object {
         fun newInstance(context: Context) = Intent(context, VtbLoginActivity::class.java)
     }
 }
+
 @Composable
 fun VtbLogin() {
     val url = "https://www.vtb.ru/"
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1d4df77..58db9cb 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -27,4 +27,5 @@
     <string name="login_by_vtb">Войти с ВТБ ID</string>
     <string name="next_step">Перейти на следующий этап</string>
     <string name="database_error">Не удалось прочитать базу данных. Попробуйте очистить данные приложения</string>
+    <string name="unknown_subscription_label">Неизвестная подписка</string>
 </resources>
\ No newline at end of file
-- 
GitLab


From 67adb656b28638ca67a8c45cac7e5d7548039f2b Mon Sep 17 00:00:00 2001
From: "nikitkagorkov@gmail.com" <gerosp>
Date: Wed, 18 Oct 2023 02:20:42 +0300
Subject: [PATCH 2/3] Added random tests

---
 .../subscriptio/domain/actions/DefinerAlg.kt  | 15 +++-
 .../subscriptio/domain/actions/Period.kt      |  8 +-
 .../domain/actions/networkUpdate.kt           | 10 ++-
 .../domain/entity/SubscriptionEntity.kt       |  4 +-
 .../presentation/AddSubscriptionActivity.kt   | 19 +++-
 .../SubscriptionDashBoardActivity.kt          | 24 +++--
 .../subscriptio/SampleGenerator.kt            | 87 +++++++++++++------
 7 files changed, 119 insertions(+), 48 deletions(-)

diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/DefinerAlg.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/DefinerAlg.kt
index 2113f5c..4b815d8 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/DefinerAlg.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/DefinerAlg.kt
@@ -1,6 +1,7 @@
 package com.threehundredbugs.subscriptio.domain.actions
 
 import android.icu.text.SimpleDateFormat
+import android.util.Log
 import com.threehundredbugs.subscriptio.data.local.dbmodel.TransactionDbModel
 import com.threehundredbugs.subscriptio.domain.entity.SubscriptionEntity
 
@@ -13,18 +14,20 @@ fun getSubscriptions(
     val sortedByTimeTransactions = transactions.sortedBy { it.bookingDateTime }.toMutableList()
     val subBuilder: MutableList<SubscriptionEntity> = mutableListOf()
     while (sortedByTimeTransactions.isNotEmpty()) {
-        val latestTransaction = sortedByTimeTransactions[0]
+        var latestTransaction = sortedByTimeTransactions[0]
         sortedByTimeTransactions.removeAt(0)
         for (transaction in sortedByTimeTransactions) {
             for (prd in Period.values()) {
                 if (nodeOfChainSubs(latestTransaction, transaction, prd)) {
                     if (prd.lst.isEmpty()) prd.lst.add(latestTransaction)
+                    latestTransaction = transaction
                     prd.lst.add(transaction)
                 }
             }
         }
         for (prd in Period.values()) {
             if (prd.lst.size >= SIMILAR_TRANSACTIONS_THRESHOLD) {
+//                latestTransaction = prd.lst[0]
                 subBuilder.add(
                     SubscriptionEntity(
                         active = prd.isActive(latestTransaction.bookingDateTime),
@@ -41,6 +44,10 @@ fun getSubscriptions(
                     )
                 )
                 sortedByTimeTransactions.removeAll(prd.lst)
+                for (period in Period.values()) {
+                    period.lst.clear()
+                }
+                break
             }
             prd.lst.clear()
         }
@@ -55,10 +62,12 @@ fun nodeOfChainSubs(
     second: TransactionDbModel,
     period: Period
 ): Boolean {
+
     return first.currency == second.currency
             &&
             first.amount == second.amount &&
-            first.merchantName == second.merchantName &&
-            period.differ(first.bookingDateTime, second.bookingDateTime)
+            first.merchantName == second.merchantName
+            && (
+            period.differ(first.bookingDateTime, second.bookingDateTime))
 }
 
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/Period.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/Period.kt
index 4781ed3..76a933b 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/Period.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/Period.kt
@@ -13,7 +13,7 @@ enum class Period {
                         secondDate / 1000
                     )
                 ) - 7L
-            ) <= 2
+            ) <= 3
 
         override fun isActive(last: Long): Boolean =
             abs(Calendar.getInstance().timeInMillis - last) <= 7
@@ -29,7 +29,7 @@ enum class Period {
                         secondDate / 1000
                     )
                 ) - 14L
-            ) <= 4
+            ) <= 5
 
         override fun isActive(last: Long): Boolean =
             abs(Calendar.getInstance().timeInMillis - last) <= 14
@@ -44,7 +44,7 @@ enum class Period {
                         secondDate / 1000
                     )
                 ) - 31L
-            ) <= 5
+            ) <= 10
 
         override fun isActive(last: Long): Boolean =
             abs(Calendar.getInstance().timeInMillis - last) <= 31
@@ -59,7 +59,7 @@ enum class Period {
                         secondDate / 1000
                     )
                 ) - 186L
-            ) <= 10
+            ) <= 15
 
         override fun isActive(last: Long): Boolean =
             abs(Calendar.getInstance().timeInMillis - last) <= 186
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/networkUpdate.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/networkUpdate.kt
index c9dd307..e43d065 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/networkUpdate.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/networkUpdate.kt
@@ -1,6 +1,7 @@
 package com.threehundredbugs.subscriptio.domain.actions
 
 import android.util.Log
+import com.threehundredbugs.subscriptio.data.local.dbmodel.TransactionDbModel
 import com.threehundredbugs.subscriptio.data.local.repository.SubscriptionRepository
 import com.threehundredbugs.subscriptio.data.local.repository.TransactionRepository
 import com.threehundredbugs.subscriptio.data.repository.TransactionRepositoryImpl
@@ -10,8 +11,9 @@ import com.threehundredbugs.subscriptio.domain.usecase.GetTransactionUseCase
 
 suspend fun networkUpdate(
     transactionRepository: TransactionRepository,
-    subscriptionRepository: SubscriptionRepository
+    subscriptionRepository: SubscriptionRepository,
 ) {
+
     val transactionUseCase = GetTransactionUseCase(TransactionRepositoryImpl())
 
     // Get updated transactions from the API
@@ -30,7 +32,7 @@ suspend fun networkUpdate(
     )
     // Update existing subscriptions and add new ones
     for (subscription in subscriptions) {
-        if (subscription.transactionDate.isEmpty()) continue
+        if (subscription.transactionDate?.isEmpty() == true || subscription.transactionDate == null) continue
         val subscriptionId =
             subscriptionRepository.getAllSubscriptions().find {
                 it.sum == subscription.sum && it.lastOrNextTransactionDate == subscription.lastOrNextTransactionDate
@@ -38,13 +40,15 @@ suspend fun networkUpdate(
             }?.id
         if (subscriptionId != null) { // if similar subscription was fund
 
-            for (transaction in subscription.transactionDate) // update it`s transactions
+            for (transaction in subscription.transactionDate!!) // update it`s transactions
                 transactionRepository.update(transaction.copy(associatedSubscriptionId = subscriptionId))
 
         } else { // if this is a new subscription
             subscriptionRepository.insertSubscription(subscription)
         }
     }
+
+
 // Testing data
 //    val transactions = transactionRepository.getAll() + TransactionDbModel(
 //        id = 0,
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/SubscriptionEntity.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/SubscriptionEntity.kt
index 72302d6..54e272e 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/SubscriptionEntity.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/SubscriptionEntity.kt
@@ -5,7 +5,6 @@ import androidx.room.Ignore
 import androidx.room.PrimaryKey
 import com.threehundredbugs.subscriptio.data.local.dbmodel.TransactionDbModel
 import java.io.Serializable
-
 @Entity(tableName = "Subscriptions")
 data class SubscriptionEntity(
 
@@ -14,7 +13,8 @@ data class SubscriptionEntity(
     val lastOrNextTransactionDate: String,
     val sum: String,
     @Ignore
-    val transactionDate: List<TransactionDbModel>,
+    @Transient
+    val transactionDate: List<TransactionDbModel>?,
     val bankCard: String,
     val subscriptionLink: String,
     @PrimaryKey(autoGenerate = true)
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/AddSubscriptionActivity.kt b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/AddSubscriptionActivity.kt
index bd7d2ea..19376b1 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/AddSubscriptionActivity.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/AddSubscriptionActivity.kt
@@ -40,6 +40,8 @@ import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import com.threehundredbugs.subscriptio.R
 import com.threehundredbugs.subscriptio.data.repository.TransactionRepositoryImpl
+import com.threehundredbugs.subscriptio.domain.entity.DataEntityField
+import com.threehundredbugs.subscriptio.domain.entity.SampleGenerator
 import com.threehundredbugs.subscriptio.domain.entity.TransactionEntity
 import com.threehundredbugs.subscriptio.domain.entity.TransactionEntityField
 import com.threehundredbugs.subscriptio.domain.usecase.GetTransactionUseCase
@@ -55,6 +57,9 @@ class AddSubscriptionActivity : ComponentActivity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+
+//        val data = SampleGenerator().generateSampleTransactionEntity(2) // TODO: Remove, debug
+
         setContent {
             SubScriptioTheme {
                 var selectedTransaction by remember {
@@ -84,6 +89,14 @@ class AddSubscriptionActivity : ComponentActivity() {
                                     loading = false
                                 }
                             }
+
+                            //debug
+
+//                            transactions = data // TODO: Remove debug code
+//
+//                            loading=false
+
+                            // End debug
                             InfinityCircularIndicator(loading)
                             AllTransactions(transactions, {
                                 selectedTransaction = selectedTransaction.toMutableList().apply {
@@ -173,7 +186,7 @@ fun AllTransactions(
                         Transaction(it, onAdd, onRemove)
                     }
                 }
-                Spacer(modifier = Modifier.padding( bottom = 6.dp))
+                Spacer(modifier = Modifier.padding(bottom = 6.dp))
 
             }
         }
@@ -231,7 +244,9 @@ fun Transaction(
             )
         }
         Text(
-            modifier = Modifier.padding(end = 6.dp, top = 5.dp).background(Color.Cyan),
+            modifier = Modifier
+                .padding(end = 6.dp, top = 5.dp)
+                .background(Color.Cyan),
             text = transaction.amount.amount + " " + transaction.amount.currency,
             fontWeight = FontWeight.SemiBold,
             fontSize = 16.sp
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/SubscriptionDashBoardActivity.kt b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/SubscriptionDashBoardActivity.kt
index 835a203..eb9689e 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/presentation/SubscriptionDashBoardActivity.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/presentation/SubscriptionDashBoardActivity.kt
@@ -56,7 +56,9 @@ import com.threehundredbugs.subscriptio.R
 import com.threehundredbugs.subscriptio.data.local.MainDatabase
 import com.threehundredbugs.subscriptio.data.local.repository.SubscriptionRepository
 import com.threehundredbugs.subscriptio.data.local.repository.TransactionRepository
+import com.threehundredbugs.subscriptio.domain.actions.getSubscriptions
 import com.threehundredbugs.subscriptio.domain.actions.networkUpdate
+import com.threehundredbugs.subscriptio.domain.entity.SampleGenerator
 import com.threehundredbugs.subscriptio.domain.entity.SubscriptionEntity
 import com.threehundredbugs.subscriptio.presentation.ui.theme.SubScriptioTheme
 import kotlinx.coroutines.launch
@@ -74,14 +76,24 @@ class SubscriptionDashBoardActivity : ComponentActivity() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         lifecycleScope.launch {
-            Log.d("UPDATE", "Network update started")
-
             networkUpdate(transactionRepository, subscriptionRepository)
-            Log.d("UPDATE", "Network update finished")
         }
+
+
+
+        val debug = false
+
+
+        val sampleTransactions = SampleGenerator().generateSampleTransactions()
+        val debugSubs = getSubscriptions(sampleTransactions.toMutableList())
+        Log.d("DEBUG", sampleTransactions.toString())
+        Log.d("DEBUG", debugSubs.toString())
+
+
         setContent {
 
             var subscription by remember {
+
                 try {
                     mutableStateOf(subscriptionRepository.getAllSubscriptions())
                 } catch (ex: Exception) {
@@ -90,8 +102,8 @@ class SubscriptionDashBoardActivity : ComponentActivity() {
                     mutableStateOf(listOf())
                 }
             }
-
-
+            if(debug)
+                subscription = debugSubs
 
             HomeScreen(subscription = subscription) {
                 subscription = subscriptionRepository.getAllSubscriptions()
@@ -410,7 +422,7 @@ fun SubscriptionCard(subscriptionEntity: SubscriptionEntity) {
                 Text(
                     text = stringResource(
                         id = if (subscriptionEntity.active) R.string.next_transaction
-                         else R.string.last_transaction
+                        else R.string.last_transaction
                     ),
                     color = MaterialTheme.colorScheme.outline,
                     fontWeight = FontWeight.SemiBold,
diff --git a/app/src/test/java/com/threehundredbugs/subscriptio/SampleGenerator.kt b/app/src/test/java/com/threehundredbugs/subscriptio/SampleGenerator.kt
index 2fbee76..e23b072 100644
--- a/app/src/test/java/com/threehundredbugs/subscriptio/SampleGenerator.kt
+++ b/app/src/test/java/com/threehundredbugs/subscriptio/SampleGenerator.kt
@@ -1,6 +1,8 @@
 package com.threehundredbugs.subscriptio
 
 import com.threehundredbugs.subscriptio.data.api.model.*
+import com.threehundredbugs.subscriptio.data.mapper.mapTransactionEntityToDbModel
+import com.threehundredbugs.subscriptio.data.mapper.mapTransactionalApiToEntity
 import org.junit.Test
 import java.io.File
 import kotlin.math.max
@@ -11,7 +13,8 @@ import kotlin.random.nextInt
 class SampleGenerator {
 
     val OPERATION_CODES = listOf("ReceivedCreditTransfer", "IssuedCreditTransfer")
-    val SUBCODES = listOf("InternalBookTransfer", "StandingOrder", "CrossBorderStandingOrder",
+    val SUBCODES = listOf(
+        "InternalBookTransfer", "StandingOrder", "CrossBorderStandingOrder",
         "SEPACreditTransfer", "DomesticCreditTransfer", "CrossBorderCreditTransfer",
         "CreditTransferWithAgreedCommercialInformation", "FinancialInstitutionCreditTransfer",
         "PriorityCreditTransfer", "PayrollSalaryPayment", "CrossBorderPayrollSalaryPayment",
@@ -19,11 +22,12 @@ class SampleGenerator {
         "ReversalDueToPaymentReturnReimbursementOfACreditTransfer",
         "CrossBorderReversalDueToPaymentReturn", "AutomaticTransfer", "ACHTransaction",
         "ACHCorporateTrade", "ACHPreAuthorised", "ACHSettlement", "ACHReturn", "ACHReversal",
-        "ACHCredit", "ACHDebit", "TreasuryTaxAndLoanService", "NordicPaymentCouncilCreditTransfer")
+        "ACHCredit", "ACHDebit", "TreasuryTaxAndLoanService", "NordicPaymentCouncilCreditTransfer"
+    )
 
     @Test
     fun generate_tests() {
-        for(j in 1..10){
+        for (j in 1..10) {
             val transactionApi = generateTransactionApi()
             File("samples${j}.out").bufferedWriter().use { out ->
                 out.write(transactionApi.toString())
@@ -31,6 +35,14 @@ class SampleGenerator {
         }
     }
 
+    fun generateSampleTransactions() {
+        for (j in 1..10) {
+            val transactionApi = generateTransactionApi()
+            val transactionData = mapTransactionalApiToEntity(transactionApi).data.transaction
+            mapTransactionEntityToDbModel(transactionData.first())
+        }
+    }
+
     fun getRandomString(length: Int): String {
         val allowedChars = ('a'..'z') + ('0'..'9')
         return (1..length).map { allowedChars.random() }.joinToString("")
@@ -51,8 +63,12 @@ class SampleGenerator {
     }
 
     fun generateAmount(): AmountField {
-        val amount = Random.nextLong(0, 10.0.pow(Random.nextInt(1, 14).toDouble()).toLong()).toString() + '.' +
-                max(0, 2 * Random.nextInt(0, 10.0.pow(Random.nextInt(0, 6).toDouble()).toInt()) - 100).toString()
+        val amount = Random.nextLong(0, 10.0.pow(Random.nextInt(1, 14).toDouble()).toLong())
+            .toString() + '.' +
+                max(
+                    0,
+                    2 * Random.nextInt(0, 10.0.pow(Random.nextInt(0, 6).toDouble()).toInt()) - 100
+                ).toString()
         val currency = listOf("RUB", "USD", "EUR", "GBP").shuffled().first()
         return AmountField(amount, currency)
     }
@@ -124,11 +140,17 @@ class SampleGenerator {
             val merchantCategoryCode = String.format("%04d", Random.nextInt(0, 10000))
             //create last transaction
             transactions.add(
-                TransactionField(accountId = accountId,
+                TransactionField(
+                    accountId = accountId,
                     creditDebitIndicator = "Debit",
                     status = "Booked",
                     amount = amount,
-                    bookingDateTime = String.format("%04d-%02d-%02dT00:00:00+00:00", year, month, day),
+                    bookingDateTime = String.format(
+                        "%04d-%02d-%02dT00:00:00+00:00",
+                        year,
+                        month,
+                        day
+                    ),
                     bankTransactionCode = BankTransactionCodeField(
                         code = "IssuedCreditTransfer",
                         subCode = "AutomaticTransfer"
@@ -140,7 +162,7 @@ class SampleGenerator {
                 )
             )
             //generate other transactions
-            for (j in 1 .. Random.nextInt(1, 15)) {
+            for (j in 1..Random.nextInt(1, 15)) {
                 val creditDebitIndicator = "Debit"
                 if (period == "WEEKLY") {
                     day -= 7
@@ -152,37 +174,43 @@ class SampleGenerator {
                             year -= 1
                         }
                     }
-                }
-                else if (period == "MONTHLY") {
+                } else if (period == "MONTHLY") {
                     month -= 1
                     if (month < 1) {
                         month += 12
                         year -= 1
                     }
-                }
-                else if (period == "3 MONTH") {
+                } else if (period == "3 MONTH") {
                     month -= 3
                     if (month < 1) {
                         month += 12
                         year -= 1
                     }
-                }
-                else {
+                } else {
                     year -= 1
                 }
-                transactions.add(TransactionField(accountId = accountId,
-                    creditDebitIndicator = creditDebitIndicator,
-                    status = "Booked",
-                    amount = amount,
-                    bookingDateTime = String.format("%04d-%02d-%02dT00:00:00+00:00", year, month, day),
-                    bankTransactionCode = BankTransactionCodeField(
-                        code = "IssuedCreditTransfer",
-                        subCode = "AutomaticTransfer"
-                    ),
-                    merchantDetails = MerchantDetailsField(
-                        merchantName = merchantName,
-                        merchantCategoryCode = merchantCategoryCode
-                    )))
+                transactions.add(
+                    TransactionField(
+                        accountId = accountId,
+                        creditDebitIndicator = creditDebitIndicator,
+                        status = "Booked",
+                        amount = amount,
+                        bookingDateTime = String.format(
+                            "%04d-%02d-%02dT00:00:00+00:00",
+                            year,
+                            month,
+                            day
+                        ),
+                        bankTransactionCode = BankTransactionCodeField(
+                            code = "IssuedCreditTransfer",
+                            subCode = "AutomaticTransfer"
+                        ),
+                        merchantDetails = MerchantDetailsField(
+                            merchantName = merchantName,
+                            merchantCategoryCode = merchantCategoryCode
+                        )
+                    )
+                )
             }
         }
         for (i in 1..Random.nextInt(1, 10000)) {
@@ -192,7 +220,10 @@ class SampleGenerator {
         val df = DataField(transactions)
         val rf = RiskField()
         val lf = LinksField("https://bank.ru/open-banking/v3.1/aisp/accounts/87659/transactions/")
-        val mf = MetaField("1", transactions.minOf { it.bookingDateTime }, transactions.maxOf { it.bookingDateTime })
+        val mf = MetaField(
+            "1",
+            transactions.minOf { it.bookingDateTime },
+            transactions.maxOf { it.bookingDateTime })
         return TransactionApi(df, rf, lf, mf)
     }
 
-- 
GitLab


From 88de3102edace84076592f8b29896126e5e9b30d Mon Sep 17 00:00:00 2001
From: "nikitkagorkov@gmail.com" <gerosp>
Date: Wed, 18 Oct 2023 09:00:10 +0300
Subject: [PATCH 3/3] Added random tests

---
 .../domain/actions/networkUpdate.kt           |   2 +-
 .../domain/entity/SampleGenerator.kt          | 240 ++++++++++++++++++
 2 files changed, 241 insertions(+), 1 deletion(-)
 create mode 100644 app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/SampleGenerator.kt

diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/networkUpdate.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/networkUpdate.kt
index e43d065..1c20334 100644
--- a/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/networkUpdate.kt
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/actions/networkUpdate.kt
@@ -49,7 +49,7 @@ suspend fun networkUpdate(
     }
 
 
-// Testing data
+// Testing data  DO NOT DELETE
 //    val transactions = transactionRepository.getAll() + TransactionDbModel(
 //        id = 0,
 //        accountId = "87659",
diff --git a/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/SampleGenerator.kt b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/SampleGenerator.kt
new file mode 100644
index 0000000..b566980
--- /dev/null
+++ b/app/src/main/java/com/threehundredbugs/subscriptio/domain/entity/SampleGenerator.kt
@@ -0,0 +1,240 @@
+package com.threehundredbugs.subscriptio.domain.entity
+
+import com.threehundredbugs.subscriptio.data.api.model.*
+import com.threehundredbugs.subscriptio.data.local.dbmodel.TransactionDbModel
+import com.threehundredbugs.subscriptio.data.mapper.mapTransactionEntityToDbModel
+import com.threehundredbugs.subscriptio.data.mapper.mapTransactionalApiToEntity
+
+import java.io.File
+import kotlin.math.max
+import kotlin.math.pow
+import kotlin.random.Random
+import kotlin.random.nextInt
+
+class SampleGenerator {
+
+    val OPERATION_CODES = listOf("ReceivedCreditTransfer", "IssuedCreditTransfer")
+    val SUBCODES = listOf(
+        "InternalBookTransfer", "StandingOrder", "CrossBorderStandingOrder",
+        "SEPACreditTransfer", "DomesticCreditTransfer", "CrossBorderCreditTransfer",
+        "CreditTransferWithAgreedCommercialInformation", "FinancialInstitutionCreditTransfer",
+        "PriorityCreditTransfer", "PayrollSalaryPayment", "CrossBorderPayrollSalaryPayment",
+        "SameDayValueCreditTransfer", "ReversalDueToPaymentCancellationRequest",
+        "ReversalDueToPaymentReturnReimbursementOfACreditTransfer",
+        "CrossBorderReversalDueToPaymentReturn", "AutomaticTransfer", "ACHTransaction",
+        "ACHCorporateTrade", "ACHPreAuthorised", "ACHSettlement", "ACHReturn", "ACHReversal",
+        "ACHCredit", "ACHDebit", "TreasuryTaxAndLoanService", "NordicPaymentCouncilCreditTransfer"
+    )
+
+
+    fun generate_tests() {
+        for (j in 1..10) {
+            val transactionApi = generateTransactionApi()
+            File("samples${j}.out").bufferedWriter().use { out ->
+                out.write(transactionApi.toString())
+            }
+        }
+    }
+
+    fun generateSampleTransactions(subscriptionsNumber: Int = 1): List<TransactionDbModel> {
+        val transactions = mutableListOf<TransactionDbModel>()
+        for (j in 1..1) {
+            val transactionApi = generateTransactionApi()
+            val transactionData = mapTransactionalApiToEntity(transactionApi).data.transaction
+            for (data in transactionData) {
+
+                transactions.add(mapTransactionEntityToDbModel(data))
+            }
+
+        }
+        return transactions
+    }
+
+    fun generateSampleTransactionEntity(iterations: Int = 100): TransactionEntity {
+        val transactionApi = generateTransactionApi(iterations)
+        return mapTransactionalApiToEntity(transactionApi)
+    }
+
+    fun getRandomString(length: Int): String {
+        val allowedChars = ('a'..'z') + ('0'..'9')
+        return (1..length).map { allowedChars.random() }.joinToString("")
+    }
+
+    fun getRandomIsoDateTime(): String {
+        val randomYear = Random.nextInt(2000, 2024)
+        val randomMonth = Random.nextInt(1, 13)
+        val randomDay = Random.nextInt(1, 29)
+        val randomHour = Random.nextInt(0, 24)
+        val randomMinute = Random.nextInt(0, 60)
+        val randomSecond = Random.nextInt(0, 60)
+        return String.format(
+            "%04d-%02d-%02dT%02d:%02d:%02d+00:00",
+            randomYear, randomMonth, randomDay,
+            randomHour, randomMinute, randomSecond
+        )
+    }
+
+    fun generateAmount(): AmountField {
+        val amount = Random.nextLong(0, 10.0.pow(Random.nextInt(1, 14).toDouble()).toLong())
+            .toString() + '.' +
+                max(
+                    0,
+                    2 * Random.nextInt(0, 10.0.pow(Random.nextInt(0, 6).toDouble()).toInt()) - 100
+                ).toString()
+        val currency = listOf("RUB", "USD", "EUR", "GBP").shuffled().first()
+        return AmountField(amount, currency)
+    }
+
+    fun generateTransaction(): TransactionField {
+        val accountId = getRandomString(Random.nextInt(1, 41))
+        val creditDebitIndicator = "Debit"
+        val status = listOf("Booked", "Pending").shuffled().first()
+        val amount = generateAmount()
+        val bookingDateTime = getRandomIsoDateTime()
+        if (Random.nextInt(0, 2) == 0) {
+            val bankTransactionCode = BankTransactionCodeField(
+                code = OPERATION_CODES.shuffled().first(),
+                subCode = SUBCODES.shuffled().first()
+            )
+            return TransactionField(
+                accountId = accountId, creditDebitIndicator = creditDebitIndicator,
+                status = status, amount = amount, bookingDateTime = bookingDateTime,
+                bankTransactionCode = bankTransactionCode
+            )
+        }
+        if (Random.nextInt(0, 2) == 0) {
+            val merchantName = getRandomString(Random.nextInt(1, 351))
+            val merchantCategoryCode = String.format("%04d", Random.nextInt(0, 10000))
+            return TransactionField(
+                accountId = accountId, creditDebitIndicator = creditDebitIndicator,
+                status = status, amount = amount, bookingDateTime = bookingDateTime,
+                merchantDetails = MerchantDetailsField(
+                    merchantName = merchantName,
+                    merchantCategoryCode = merchantCategoryCode
+                )
+            )
+        }
+        if (Random.nextInt(0, 4) == 0) {
+            val bankTransactionCode = BankTransactionCodeField(
+                code = OPERATION_CODES.shuffled().first(),
+                subCode = SUBCODES.shuffled().first()
+            )
+            val merchantName = getRandomString(Random.nextInt(1, 351))
+            val merchantCategoryCode = String.format("%04d", Random.nextInt(0, 10000))
+            return TransactionField(
+                accountId = accountId, creditDebitIndicator = creditDebitIndicator,
+                status = status, amount = amount, bookingDateTime = bookingDateTime,
+                bankTransactionCode = bankTransactionCode,
+                merchantDetails = MerchantDetailsField(
+                    merchantName = merchantName,
+                    merchantCategoryCode = merchantCategoryCode
+                )
+            )
+        }
+        return TransactionField(
+            accountId = accountId, creditDebitIndicator = creditDebitIndicator,
+            status = status, amount = amount, bookingDateTime = bookingDateTime
+        )
+    }
+
+    fun generateTransactionApi(iterations: Int = 100): TransactionApi {
+        val transactions = mutableListOf<TransactionField>()
+        for (i in 1..Random.nextInt(1, 3)) {
+            // choose a random subscription period
+            val period = listOf("WEEKLY").shuffled().first()
+            // choose a random subscription amount
+            val amount = generateAmount()
+            val accountId = getRandomString(Random.nextInt(1, 41))
+            var day = Random.nextInt(1, 29)
+            var month = Random.nextInt(1, 12)
+            var year = Random.nextInt(2000, 2023)
+            val merchantName = "Subscription $i"
+            val merchantCategoryCode = String.format("%04d", Random.nextInt(0, 10000))
+            //create last transaction
+            transactions.add(
+                TransactionField(
+                    accountId = accountId,
+                    creditDebitIndicator = "Debit",
+                    status = "Booked",
+                    amount = amount,
+                    bookingDateTime = String.format(
+                        "%04d-%02d-%02dT00:00:00+00:00",
+                        year,
+                        month,
+                        day
+                    ),
+                    bankTransactionCode = BankTransactionCodeField(
+                        code = "IssuedCreditTransfer",
+                        subCode = "AutomaticTransfer"
+                    ),
+                    merchantDetails = MerchantDetailsField(
+                        merchantName = merchantName,
+                        merchantCategoryCode = merchantCategoryCode
+                    )
+                )
+            )
+            //generate other transactions
+            for (j in 1..4) {
+                val creditDebitIndicator = "Debit"
+                if (period == "WEEKLY") {
+                    day -= 7
+                    if (day < 1) {
+                        day += 30
+                        month -= 1
+                        if (month < 1) {
+                            month += 12
+                            year -= 1
+                        }
+                    }
+                } else if (period == "MONTHLY") {
+                    month -= 1
+                    if (month < 1) {
+                        month += 12
+                        year -= 1
+                    }
+                } else if (period == "3 MONTH") {
+                    month -= 3
+                    if (month < 1) {
+                        month += 12
+                        year -= 1
+                    }
+                } else {
+                    year -= 1
+                }
+                transactions.add(
+                    TransactionField(
+                        accountId = accountId,
+                        creditDebitIndicator = creditDebitIndicator,
+                        status = "Booked",
+                        amount = amount,
+                        bookingDateTime = String.format(
+                            "%04d-%02d-%02dT00:00:00+00:00",
+                            year,
+                            month,
+                            day
+                        ),
+                        bankTransactionCode = BankTransactionCodeField(
+                            code = "IssuedCreditTransfer",
+                            subCode = "AutomaticTransfer"
+                        ),
+                        merchantDetails = MerchantDetailsField(
+                            merchantName = merchantName,
+                            merchantCategoryCode = merchantCategoryCode
+                        )
+                    )
+                )
+            }
+        }
+
+        transactions.sortBy { it.bookingDateTime }
+        val df = DataField(transactions)
+        val rf = RiskField()
+        val lf = LinksField("https://bank.ru/open-banking/v3.1/aisp/accounts/87659/transactions/")
+        val mf = MetaField(
+            "1",
+            transactions.minOf { it.bookingDateTime },
+            transactions.maxOf { it.bookingDateTime })
+        return TransactionApi(df, rf, lf, mf)
+    }
+
+}
-- 
GitLab