diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ba7f073466..b26d2f8a9b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,5 @@ ## Pull Request (PR) Checklist - Please check if your pull request fulfills the following requirements: - - [ ] The PR is submitted to the `develop` branch. - [ ] I've read the **[Contribution Guidelines](https://github.com/ILIYANGERMANOV/ivy-wallet/blob/main/CONTRIBUTING.md)**. - [ ] The code builds and is tested on an actual Android device. @@ -12,41 +10,16 @@ Please check if your pull request fulfills the following requirements: _Important: Don't worry if you experience flaky UI tests. Just re-run the failed ones again and if they pass => it's all good!_ _Put an `x` in the boxes that apply._ - - [x] Demo: Checking checkbox using `[x]` -### How to run Ivy Wallet's UI tests (`androidTest`) - -**Connect Android Emulator** -- Pixel 5 API 29+ AVD emulator _(recommended)_ -- Pixel 3XL API 29+ AVD emulator _(recommended)_ -- Large screen physical device _(might also work)_ - -**Method 1: Android Studio UI** -- Find `com (androidTest)` package -- Right click -- `Run 'Tests in 'com''` - -_Note: If you've checked "Compact Middle Packages" the option will appear as `com.ivy.wallet (androidTest)`._ - -**Method 2: Gradle Wrapper** -- `chmod +x gradlew` (Linux) -- `./gradlew connectedDebugAndroidTest` - -**Method 3: Fastlane** -- Install Ruby 2.7 -- `bundle install` -- `bundle exec fastlane ui_tests` ## Pull Request Type - Please check the type of change your PR introduces: - - [ ] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes) -- [ ] Small update (fix typo, UI fine-tune, change color or something small) +- [ ] Small improvement (fix typo, UI fine-tune, change color or something small) - [ ] Gradle Build related changes - [ ] Dependencies update (updating libraries) - [ ] Documentation @@ -55,18 +28,37 @@ Please check the type of change your PR introduces: _Put an `x` in the boxes that apply._ -## Does this PR closes any GitHub Issues? +## Does this PR closes any GitHub Issues? Check **[Ivy Wallet Issues](https://github.com/ILIYANGERMANOV/ivy-wallet/issues)**. - - Closes #N/A (type issue number here) -## What's changed? +## What's changed? Describe with a few bullets **what's new:** +- a +- b +- c +- d + +## How to run Ivy Wallet's UI tests (`androidTest`) +**Connect Android Emulator** +- Pixel 5 API 29+ AVD emulator _(recommended)_ +- Pixel 3XL API 29+ AVD emulator _(recommended)_ +- Large screen physical device _(might also work)_ + +**Method 1: Android Studio UI** +- Find `com (androidTest)` package +- Right click +- `Run 'Tests in 'com''` + +_Note: If you've checked "Compact Middle Packages" the option will appear as `com.ivy.wallet (androidTest)`._ -- -- -- +**Method 2: Gradle Wrapper** +- `chmod +x gradlew` (Linux) +- `./gradlew connectedDebugAndroidTest` -_This PR template style was inspired by [ionic-framework](https://github.com/ionic-team/ionic-framework/blob/main/.github/PULL_REQUEST_TEMPLATE.md)._ \ No newline at end of file +**Method 3: Fastlane** +- Install Ruby 2.7 +- `bundle install` +- `bundle exec fastlane ui_tests` \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/logic/csv/CSVImporter.kt b/app/src/main/java/com/ivy/wallet/logic/csv/CSVImporter.kt index 31d4c04792..96fd4e540d 100644 --- a/app/src/main/java/com/ivy/wallet/logic/csv/CSVImporter.kt +++ b/app/src/main/java/com/ivy/wallet/logic/csv/CSVImporter.kt @@ -171,14 +171,18 @@ class CSVImporter( val dateTime = mapDate( - if (rowMapping.timeOnly != null) { + rowMapping = rowMapping, + dateString = if (rowMapping.timeOnly != null) { // date and time are separated in csv, join them with space row.extract(rowMapping.date) + " " + row.extract(rowMapping.timeOnly) } else { row.extract(rowMapping.date) } ) - val dueDate = mapDate(row.extract(rowMapping.dueDate)) + val dueDate = mapDate( + rowMapping = rowMapping, + dateString = row.extract(rowMapping.dueDate) + ) if (dateTime == null && dueDate == null) { //Cannot save transactions without any date return null @@ -241,9 +245,11 @@ class CSVImporter( normalizedType.contains("income") -> TransactionType.INCOME normalizedType.contains("expense") -> TransactionType.EXPENSE normalizedType.contains("transfer") -> TransactionType.TRANSFER - // Default to Expense because Financisto messed up its CSV Export - // and mixes it with another (ignored) column - else -> TransactionType.EXPENSE + else -> { + // Default to Expense because Financisto messed up its CSV Export + // and mixes it with another (ignored) column + if (rowMapping.defaultTypeToExpense) TransactionType.EXPENSE else null + } } } @@ -255,9 +261,28 @@ class CSVImporter( .toDoubleOrNull() } - private fun mapDate(dateString: String?): LocalDateTime? { + private fun mapDate( + rowMapping: RowMapping, + dateString: String? + ): LocalDateTime? { if (dateString == null || dateString.isBlank()) return null + if (rowMapping.dateOnlyFormat != null) { + try { + return dateString.parseDateOnly(rowMapping.dateOnlyFormat) + } catch (e: Exception) { + e.printStackTrace() + } + } + + if (rowMapping.dateTimeFormat != null) { + try { + return dateString.parseDateTime(rowMapping.dateTimeFormat) + } catch (e: Exception) { + e.printStackTrace() + } + } + val supportedPatterns = listOf( "dd/MM/yyyy HH:mm", "dd/MM/yyyy HH:mm:ss", @@ -316,10 +341,7 @@ class CSVImporter( for (pattern in supportedPatterns) { try { - return LocalDateTime.parse( - dateString, - DateTimeFormatter.ofPattern(pattern) - ).convertLocalToUTC() + return dateString.parseDateTime(dateTimeFormat = pattern) } catch (e: Exception) { } } @@ -333,11 +355,7 @@ class CSVImporter( for (pattern in supportedDateOnlyPatterns) { try { - return LocalDate.parse( - dateString, - DateTimeFormatter.ofPattern(pattern) - ).atTime(12, 0) - .convertLocalToUTC() + return dateString.parseDateOnly(dateFormat = pattern) } catch (e: Exception) { } } @@ -367,6 +385,24 @@ class CSVImporter( .minusYears(1) } + private fun String.parseDateOnly( + dateFormat: String + ): LocalDateTime { + return LocalDate.parse( + this, + DateTimeFormatter.ofPattern(dateFormat) + ).atTime(12, 0).convertLocalToUTC() + } + + private fun String.parseDateTime( + dateTimeFormat: String + ): LocalDateTime { + return LocalDateTime.parse( + this, + DateTimeFormatter.ofPattern(dateTimeFormat) + ).convertLocalToUTC() + } + private fun mapAccount( baseCurrency: String, accountNameString: String?, diff --git a/app/src/main/java/com/ivy/wallet/logic/csv/CSVMapper.kt b/app/src/main/java/com/ivy/wallet/logic/csv/CSVMapper.kt index 9ff1209a1d..13b66caa67 100644 --- a/app/src/main/java/com/ivy/wallet/logic/csv/CSVMapper.kt +++ b/app/src/main/java/com/ivy/wallet/logic/csv/CSVMapper.kt @@ -18,9 +18,10 @@ class CSVMapper { ivyMappingV1() } } - ImportType.MONEY_MANAGER_PRASE -> moneyManagerPrase() + ImportType.MONEY_MANAGER -> moneyManager() ImportType.WALLET_BY_BUDGET_BAKERS -> walletByBudgetBakers() ImportType.SPENDEE -> spendee() + ImportType.MONEFY -> monefy() ImportType.ONE_MONEY -> oneMoney() ImportType.BLUE_COINS -> blueCoins() ImportType.KTW_MONEY_MANAGER -> ktwMoneyManager() @@ -74,7 +75,7 @@ class CSVMapper { ) //Praseto - https://play.google.com/store/apps/details?id=com.realbyteapps.moneymanagerfree&hl=en&gl=US - private fun moneyManagerPrase() = RowMapping( + private fun moneyManager() = RowMapping( type = 6, amount = 8, account = 1, @@ -123,6 +124,26 @@ class CSVMapper { title = 6 ) + private fun monefy() = RowMapping( + date = 0, + dateOnlyFormat = "dd/MM/yyyy", + account = 1, + category = 2, + amount = 3, + accountCurrency = 4, + //converted amount = 5 + //currency = 6 + title = 7, + defaultTypeToExpense = true, //Monefy doesn't have transaction type, it uses amount +/- sign, + + transformTransaction = { transaction, _, csvAmount -> + //Monefy doesn't have transaction type, it uses amount +/- sign + transaction.copy( + type = if (csvAmount > 0) TransactionType.INCOME else TransactionType.EXPENSE + ) + } + ) + private fun oneMoney() = RowMapping( date = 0, type = 1, @@ -188,6 +209,7 @@ class CSVMapper { // parent transaction = 8 title = 9, type = 10, + defaultTypeToExpense = true, // project = 11 description = 12, diff --git a/app/src/main/java/com/ivy/wallet/logic/csv/model/ImportType.kt b/app/src/main/java/com/ivy/wallet/logic/csv/model/ImportType.kt index e6dfec4268..8ddba26d87 100644 --- a/app/src/main/java/com/ivy/wallet/logic/csv/model/ImportType.kt +++ b/app/src/main/java/com/ivy/wallet/logic/csv/model/ImportType.kt @@ -9,9 +9,10 @@ import com.ivy.wallet.ui.theme.* enum class ImportType { IVY, - MONEY_MANAGER_PRASE, + MONEY_MANAGER, WALLET_BY_BUDGET_BAKERS, SPENDEE, + MONEFY, ONE_MONEY, BLUE_COINS, KTW_MONEY_MANAGER, @@ -20,9 +21,10 @@ enum class ImportType { fun color(): Color = when (this) { IVY -> Ivy - MONEY_MANAGER_PRASE -> Red + MONEY_MANAGER -> Red WALLET_BY_BUDGET_BAKERS -> Green SPENDEE -> RedLight + MONEFY -> Green ONE_MONEY -> Red3 BLUE_COINS -> Blue KTW_MONEY_MANAGER -> Yellow @@ -32,9 +34,10 @@ enum class ImportType { fun appId(): String = when (this) { IVY -> "com.ivy.wallet" - MONEY_MANAGER_PRASE -> "com.realbyteapps.moneymanagerfree" + MONEY_MANAGER -> "com.realbyteapps.moneymanagerfree" WALLET_BY_BUDGET_BAKERS -> "com.droid4you.application.wallet" SPENDEE -> "com.cleevio.spendee" + MONEFY -> "com.monefy.app.lite" ONE_MONEY -> "org.pixelrush.moneyiq" BLUE_COINS -> "com.rammigsoftware.bluecoins" KTW_MONEY_MANAGER -> "com.ktwapps.walletmanager" @@ -44,10 +47,11 @@ enum class ImportType { @DrawableRes fun logo(): Int = when (this) { - IVY -> R.drawable.ivy_wallet_logo_import - MONEY_MANAGER_PRASE -> R.drawable.ic_money_manager_prase + IVY -> R.drawable.ivywallet_logo + MONEY_MANAGER -> R.drawable.moneymanager_logo WALLET_BY_BUDGET_BAKERS -> R.drawable.wallet_by_budgetbakers_logo - SPENDEE -> R.drawable.speende_logo_png + SPENDEE -> R.drawable.spendee_logo + MONEFY -> R.drawable.monefy_logo ONE_MONEY -> R.drawable.one_money_logo BLUE_COINS -> R.drawable.bluecoins KTW_MONEY_MANAGER -> R.drawable.ktw_money_manager_logo @@ -57,9 +61,10 @@ enum class ImportType { fun listName(): String = when (this) { IVY -> "Ivy Wallet CSV" - MONEY_MANAGER_PRASE -> "Money Manager" + MONEY_MANAGER -> "Money Manager" WALLET_BY_BUDGET_BAKERS -> "Wallet by BudgetBakers" SPENDEE -> "Spendee" + MONEFY -> "Monefy" ONE_MONEY -> "1Money" BLUE_COINS -> "Bluecoins Finance" KTW_MONEY_MANAGER -> "Money Manager (KTW)" @@ -69,14 +74,7 @@ enum class ImportType { fun appName(): String = when (this) { IVY -> "Ivy Wallet" - MONEY_MANAGER_PRASE -> "Money Manager" - WALLET_BY_BUDGET_BAKERS -> "Wallet by BudgetBakers" - SPENDEE -> "Spendee" - ONE_MONEY -> "1Money" - BLUE_COINS -> "Bluecoins Finance" - KTW_MONEY_MANAGER -> "Money Manager (KTW)" - FORTUNE_CITY -> "Fortune City" - FINANCISTO -> "Financisto" + else -> listName() } @Composable @@ -89,7 +87,7 @@ enum class ImportType { onUploadClick = onUploadClick ) } - MONEY_MANAGER_PRASE -> { + MONEY_MANAGER -> { MoneyManagerPraseSteps( onUploadClick = onUploadClick ) @@ -102,6 +100,9 @@ enum class ImportType { SPENDEE -> SpendeeSteps( onUploadClick = onUploadClick ) + MONEFY -> MonefySteps( + onUploadClick = onUploadClick + ) ONE_MONEY -> OneMoneySteps( onUploadClick = onUploadClick ) diff --git a/app/src/main/java/com/ivy/wallet/logic/csv/model/RowMapping.kt b/app/src/main/java/com/ivy/wallet/logic/csv/model/RowMapping.kt index 17a15c70ab..9fdd0bf250 100644 --- a/app/src/main/java/com/ivy/wallet/logic/csv/model/RowMapping.kt +++ b/app/src/main/java/com/ivy/wallet/logic/csv/model/RowMapping.kt @@ -5,6 +5,7 @@ import com.ivy.wallet.model.entity.Transaction data class RowMapping( val type: Int? = null, + val defaultTypeToExpense: Boolean = false, val amount: Int, val account: Int, @@ -14,6 +15,8 @@ data class RowMapping( val accountIcon: Int? = null, val date: Int, + val dateOnlyFormat: String? = null, + val dateTimeFormat: String? = null, val timeOnly: Int? = null, val dueDate: Int? = null, diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/ImportInstructions.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/ImportInstructions.kt index 3abfc19e04..abef498ee3 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/ImportInstructions.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/ImportInstructions.kt @@ -355,7 +355,7 @@ private fun Preview() { IvyAppPreview { ImportInstructions( hasSkip = true, - importType = ImportType.MONEY_MANAGER_PRASE, + importType = ImportType.MONEY_MANAGER, onSkip = {} ) { diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/MonefySteps.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/MonefySteps.kt new file mode 100644 index 0000000000..a32953f21b --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/MonefySteps.kt @@ -0,0 +1,27 @@ +package com.ivy.wallet.ui.csvimport.flow.instructions + +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun MonefySteps( + onUploadClick: () -> Unit +) { + Spacer(Modifier.height(12.dp)) + + StepTitle( + number = 1, + title = "Export to file", + description = "Character set: UTF-8\nDecimal separator: Decimal point '.'\nDelimiter character: Comma ','" + ) + + Spacer(Modifier.height(24.dp)) + + UploadFileStep( + stepNumber = 2, + onUploadClick = onUploadClick + ) +} \ No newline at end of file diff --git a/app/src/main/res/drawable-nodpi/ivywallet_logo.png b/app/src/main/res/drawable-nodpi/ivywallet_logo.png new file mode 100644 index 0000000000..5c7dcd3203 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/ivywallet_logo.png differ diff --git a/app/src/main/res/drawable-nodpi/monefy_logo.png b/app/src/main/res/drawable-nodpi/monefy_logo.png new file mode 100644 index 0000000000..d8e502fad8 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/monefy_logo.png differ diff --git a/app/src/main/res/drawable-nodpi/moneymanager_logo.png b/app/src/main/res/drawable-nodpi/moneymanager_logo.png new file mode 100644 index 0000000000..f312b31ff7 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/moneymanager_logo.png differ diff --git a/app/src/main/res/drawable-nodpi/spendee_logo.png b/app/src/main/res/drawable-nodpi/spendee_logo.png new file mode 100644 index 0000000000..5d480a9fe9 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/spendee_logo.png differ diff --git a/app/src/main/res/drawable-nodpi/wallet_by_budgetbakers_logo.png b/app/src/main/res/drawable-nodpi/wallet_by_budgetbakers_logo.png new file mode 100644 index 0000000000..1a8e14d67b Binary files /dev/null and b/app/src/main/res/drawable-nodpi/wallet_by_budgetbakers_logo.png differ diff --git a/app/src/main/res/drawable/ic_money_manager_prase.xml b/app/src/main/res/drawable/ic_money_manager_prase.xml deleted file mode 100644 index 4d07a5e058..0000000000 --- a/app/src/main/res/drawable/ic_money_manager_prase.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ivy_wallet_logo_import.xml b/app/src/main/res/drawable/ivy_wallet_logo_import.xml deleted file mode 100644 index 55ebf179df..0000000000 --- a/app/src/main/res/drawable/ivy_wallet_logo_import.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/speende_logo_png.png b/app/src/main/res/drawable/speende_logo_png.png deleted file mode 100644 index 59f526e8a5..0000000000 Binary files a/app/src/main/res/drawable/speende_logo_png.png and /dev/null differ diff --git a/app/src/main/res/drawable/wallet_by_budgetbakers_logo.png b/app/src/main/res/drawable/wallet_by_budgetbakers_logo.png deleted file mode 100644 index 38ffa52f2a..0000000000 Binary files a/app/src/main/res/drawable/wallet_by_budgetbakers_logo.png and /dev/null differ