From 7e8cc57265712c998ae4dffa2b54d9de74962088 Mon Sep 17 00:00:00 2001 From: Shamim Shahrier Emon Date: Mon, 30 Sep 2024 23:00:49 +0600 Subject: [PATCH] [Feature] Added Decimal Formatting Capability (#3568) * Under dev * Under dev * Decimal number capability added * Decimal number capability added : Reviews resolved --- .../java/com/ivy/domain/features/Features.kt | 1 + .../com/ivy/domain/features/IvyFeatures.kt | 16 +++- .../java/com/ivy/ui/FormatMoneyUseCase.kt | 29 ++++++++ .../java/com/ivy/ui/FormatMoneyUseCaseTest.kt | 73 +++++++++++++++++++ 4 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 shared/ui/core/src/main/java/com/ivy/ui/FormatMoneyUseCase.kt create mode 100644 shared/ui/core/src/test/java/com/ivy/ui/FormatMoneyUseCaseTest.kt diff --git a/shared/domain/src/main/java/com/ivy/domain/features/Features.kt b/shared/domain/src/main/java/com/ivy/domain/features/Features.kt index fedb4c25f7..af043582b2 100644 --- a/shared/domain/src/main/java/com/ivy/domain/features/Features.kt +++ b/shared/domain/src/main/java/com/ivy/domain/features/Features.kt @@ -7,6 +7,7 @@ interface Features { val showTitleSuggestions: BoolFeature val showCategorySearchBar: BoolFeature val hideTotalBalance: BoolFeature + val showDecimalNumber: BoolFeature val allFeatures: List } diff --git a/shared/domain/src/main/java/com/ivy/domain/features/IvyFeatures.kt b/shared/domain/src/main/java/com/ivy/domain/features/IvyFeatures.kt index c2f80701c7..900a0b9b47 100644 --- a/shared/domain/src/main/java/com/ivy/domain/features/IvyFeatures.kt +++ b/shared/domain/src/main/java/com/ivy/domain/features/IvyFeatures.kt @@ -8,7 +8,7 @@ class IvyFeatures @Inject constructor() : Features { override val sortCategoriesAlphabetically = BoolFeature( key = "sort_categories_alphabetically", - group = FeatureGroup.Other, + group = FeatureGroup.Category, name = "Sort Categories Alphabetically", description = "Sort income and expenses" + " categories alphabetically" @@ -52,6 +52,14 @@ class IvyFeatures @Inject constructor() : Features { defaultValue = false ) + override val showDecimalNumber = BoolFeature( + key = "show_decimal_number", + group = FeatureGroup.Other, + name = "Show Decimal Number", + description = "Whether to show the decimal part in amounts", + defaultValue = true + ) + override val allFeatures: List get() = listOf( sortCategoriesAlphabetically, @@ -59,6 +67,10 @@ class IvyFeatures @Inject constructor() : Features { compactCategoriesMode, showTitleSuggestions, showCategorySearchBar, - hideTotalBalance + hideTotalBalance, + /* will be uncommented when this functionality + * will be available across the application in up-coming PRs + showDecimalNumber + */ ) } diff --git a/shared/ui/core/src/main/java/com/ivy/ui/FormatMoneyUseCase.kt b/shared/ui/core/src/main/java/com/ivy/ui/FormatMoneyUseCase.kt new file mode 100644 index 0000000000..bc0114e044 --- /dev/null +++ b/shared/ui/core/src/main/java/com/ivy/ui/FormatMoneyUseCase.kt @@ -0,0 +1,29 @@ +package com.ivy.ui + +import android.content.Context +import com.ivy.domain.features.Features +import com.ivy.ui.time.DevicePreferences +import dagger.hilt.android.qualifiers.ApplicationContext +import java.text.DecimalFormat +import java.text.DecimalFormatSymbols +import javax.inject.Inject + +class FormatMoneyUseCase @Inject constructor( + private val features: Features, + private val devicePreferences: DevicePreferences, + @ApplicationContext private val context: Context +) { + + private val locale = devicePreferences.locale() + private val withoutDecimalFormatter = DecimalFormat("###,###", DecimalFormatSymbols(locale)) + private val withDecimalFormatter = DecimalFormat("###,###.00", DecimalFormatSymbols(locale)) + + suspend fun format(value: Double): String { + val showDecimalPoint = features.showDecimalNumber.isEnabled(context) + + return when (showDecimalPoint) { + true -> withDecimalFormatter.format(value) + false -> withoutDecimalFormatter.format(value) + } + } +} \ No newline at end of file diff --git a/shared/ui/core/src/test/java/com/ivy/ui/FormatMoneyUseCaseTest.kt b/shared/ui/core/src/test/java/com/ivy/ui/FormatMoneyUseCaseTest.kt new file mode 100644 index 0000000000..4a98251c3f --- /dev/null +++ b/shared/ui/core/src/test/java/com/ivy/ui/FormatMoneyUseCaseTest.kt @@ -0,0 +1,73 @@ +package com.ivy.ui + +import android.content.Context +import com.google.testing.junit.testparameterinjector.TestParameter +import com.google.testing.junit.testparameterinjector.TestParameterInjector +import com.ivy.domain.features.Features +import com.ivy.ui.time.DevicePreferences +import io.kotest.common.runBlocking +import io.kotest.matchers.shouldBe +import io.mockk.coEvery +import io.mockk.every +import io.mockk.mockk +import org.junit.Test +import org.junit.runner.RunWith +import java.util.Locale + +@RunWith(TestParameterInjector::class) +class FormatMoneyUseCaseTest { + + private val features = mockk() + private val devicePreferences = mockk() + + enum class MoneyFormatterTestCase( + val amount: Double, + val showDecimal: Boolean, + val locale: Locale, + val expectedOutput: String + ) { + ENG_SHOW_DECIMAL( + amount = 1000.12, + showDecimal = true, + locale = Locale.ENGLISH, + expectedOutput = "1,000.12" + ), + ENG_HIDE_DECIMAL( + amount = 1000.12, + showDecimal = false, + locale = Locale.ENGLISH, + expectedOutput = "1,000" + ), + GERMAN_SHOW_DECIMAL( + amount = 1000.12, + showDecimal = true, + locale = Locale.GERMAN, + expectedOutput = "1.000,12" + ), + GERMAN_HIDE_DECIMAL( + amount = 1000.12, + showDecimal = false, + locale = Locale.GERMAN, + expectedOutput = "1.000" + ), + } + + private lateinit var formatMoneyUseCase: FormatMoneyUseCase + + @Test + fun `validate decimal formatting`( + @TestParameter testCase: MoneyFormatterTestCase + ): Unit = runBlocking { + // given + val context = mockk() + every { features.showDecimalNumber } returns mockk { coEvery { isEnabled(any()) } returns testCase.showDecimal } + every { devicePreferences.locale() } returns testCase.locale + formatMoneyUseCase = FormatMoneyUseCase(features, devicePreferences, context) + + // when + val result = formatMoneyUseCase.format(value = testCase.amount) + + // then + result shouldBe testCase.expectedOutput + } +} \ No newline at end of file