Fix currency and added user weighting

This commit is contained in:
2026-09-03 22:00:26 +02:00
parent 488aa9bba6
commit 492230faa2
5 changed files with 123 additions and 23 deletions
@@ -333,7 +333,7 @@ class EditBillActivity : AppCompatActivity() {
viewModel.owersSelection[id] == true && viewModel.parseAmountFromUi(amountStr) > 0
}.mapValues {
val uiAmount = viewModel.parseAmountFromUi(it.value)
SupportUtil.round2(uiAmount / viewModel.selectedCurrencyRate)
SupportUtil.round2(uiAmount * viewModel.selectedCurrencyRate)
}
} else { // PERCENT
val totalAmount = viewModel.amountAsDouble
@@ -342,7 +342,7 @@ class EditBillActivity : AppCompatActivity() {
}.mapValues {
val percent = viewModel.parseAmountFromUi(it.value)
val uiAmount = SupportUtil.round2(totalAmount * percent / 100.0)
SupportUtil.round2(uiAmount / viewModel.selectedCurrencyRate)
SupportUtil.round2(uiAmount * viewModel.selectedCurrencyRate)
}
}
@@ -450,7 +450,10 @@ fun OwerSelectionSection(
size = 32.dp
)
Spacer(modifier = Modifier.width(8.dp))
Text(member.name, modifier = Modifier.weight(1f))
val weightSuffix = if (viewModel.hasDifferentWeights && member.weight != 1.0) {
" (x${member.weight.toString().removeSuffix(".0")})"
} else ""
Text("${member.name}$weightSuffix", modifier = Modifier.weight(1f))
if (isSelected || viewModel.splitMode != SplitMode.EVEN) {
val focusManager = LocalFocusManager.current
@@ -671,19 +674,115 @@ fun BillAdditionalDetailsSection(
}
@SuppressLint("ViewModelConstructorInComposable")
@Preview(showBackground = true)
@Preview(showBackground = true, name = "Standard Split")
@Composable
fun EditBillScreenPreview() {
MaterialTheme {
EditBillScreen(
viewModel = EditBillViewModel().apply {
what = "Pizza"
amount = "12.50"
amount = "12.00"
mainCurrencyName = "EUR"
members = listOf(
DBMember(1, 0, 0, "Alice", true, 1.0, 0, null, null, null, null, null),
DBMember(2, 0, 0, "Bob", true, 1.0, 0, null, null, null, null, null)
)
owersSelection[1] = true
owersSelection[2] = true
updateSplits()
},
categories = emptyList(),
paymentModes = emptyList(),
onSave = {},
onBack = {},
onDateClick = {},
onTimeClick = {},
onScan = {}
)
}
}
@SuppressLint("ViewModelConstructorInComposable")
@Preview(showBackground = true, name = "Weighted Split")
@Composable
fun EditBillScreenWeightedPreview() {
MaterialTheme {
EditBillScreen(
viewModel = EditBillViewModel().apply {
what = "Weighted Pizza"
amount = "60.00"
mainCurrencyName = "EUR"
members = listOf(
DBMember(1, 0, 0, "Alice", true, 2.0, 0, null, null, null, null, null),
DBMember(2, 0, 0, "Bob", true, 1.0, 0, null, null, null, null, null),
DBMember(3, 0, 0, "Charlie", true, 1.0, 0, null, null, null, null, null)
)
owersSelection[1] = true
owersSelection[2] = true
owersSelection[3] = true
updateSplits()
},
categories = emptyList(),
paymentModes = emptyList(),
onSave = {},
onBack = {},
onDateClick = {},
onTimeClick = {},
onScan = {}
)
}
}
@SuppressLint("ViewModelConstructorInComposable")
@Preview(showBackground = true, name = "Custom Split (#)")
@Composable
fun EditBillScreenCustomPreview() {
MaterialTheme {
EditBillScreen(
viewModel = EditBillViewModel().apply {
what = "Custom Split Pizza"
amount = "60.00"
mainCurrencyName = "EUR"
members = listOf(
DBMember(1, 0, 0, "Alice", true, 1.0, 0, null, null, null, null, null),
DBMember(2, 0, 0, "Bob", true, 1.0, 0, null, null, null, null, null)
)
splitMode = SplitMode.CUSTOM
owersSelection[1] = true
owersSelection[2] = true
owersCustomSplit[1] = "40.00"
owersCustomSplit[2] = "20.00"
},
categories = emptyList(),
paymentModes = emptyList(),
onSave = {},
onBack = {},
onDateClick = {},
onTimeClick = {},
onScan = {}
)
}
}
@SuppressLint("ViewModelConstructorInComposable")
@Preview(showBackground = true, name = "Percent Split (%)")
@Composable
fun EditBillScreenPercentPreview() {
MaterialTheme {
EditBillScreen(
viewModel = EditBillViewModel().apply {
what = "Percent Split Pizza"
amount = "100.00"
mainCurrencyName = "EUR"
members = listOf(
DBMember(1, 0, 0, "Alice", true, 1.0, 0, null, null, null, null, null),
DBMember(2, 0, 0, "Bob", true, 1.0, 0, null, null, null, null, null)
)
splitMode = SplitMode.PERCENT
owersSelection[1] = true
owersSelection[2] = true
owersPercentSplit[1] = "70"
owersPercentSplit[2] = "30"
},
categories = emptyList(),
paymentModes = emptyList(),
@@ -61,18 +61,18 @@ class EditBillViewModel : ViewModel() {
val amountAsDouble: Double
get() = parseAmountFromUi(amount)
fun getEvenSplit(): Double {
val selectedOwersCount = owersSelection.count { it.value }
return if (selectedOwersCount > 0) SupportUtil.round2(amountAsDouble / selectedOwersCount) else 0.0
}
val hasDifferentWeights: Boolean
get() = members.isNotEmpty() && members.any { it.weight != members[0].weight }
fun updateSplits() {
if (splitMode == SplitMode.EVEN) {
val even = getEvenSplit()
val evenStr = if (even == 0.0) "" else SupportUtil.round2(even).toString()
val selectedMembers = members.filter { owersSelection[it.id] == true }
val totalWeight = selectedMembers.sumOf { it.weight }
val uiAmount = amountAsDouble
members.forEach { m ->
if (owersSelection[m.id] == true) {
owersCustomSplit[m.id] = evenStr
val share = if (totalWeight > 0) (uiAmount * m.weight) / totalWeight else 0.0
owersCustomSplit[m.id] = if (share == 0.0) "" else SupportUtil.round2(share).toString()
} else {
owersCustomSplit.remove(m.id)
}
@@ -180,7 +180,7 @@ class EditBillViewModel : ViewModel() {
}
fun getFinalAmount(): Double {
return SupportUtil.round2(amountAsDouble / selectedCurrencyRate)
return SupportUtil.round2(amountAsDouble * selectedCurrencyRate)
}
fun getFinalComment(): String {
@@ -252,10 +252,9 @@ class EditBillViewModel : ViewModel() {
if (selected) {
// If we have metadata, the custom splits from DB are also converted.
// We should show them as "Original" if possible?
// Actually, if we use metadata, we should probably store original splits too,
// but for now let's just reverse the rate for display.
// Reverse calculation for display: UI Part = DB Part / Rate
val dbPart = customSplits[member.id]!!
val uiPart = if (selectedCurrencyRate != 1.0) dbPart * selectedCurrencyRate else dbPart
val uiPart = if (selectedCurrencyRate != 0.0) dbPart / selectedCurrencyRate else dbPart
owersCustomSplit[member.id] = SupportUtil.round2(uiPart).toString()
}
}
@@ -263,18 +262,18 @@ class EditBillViewModel : ViewModel() {
// Even split logic
splitMode = SplitMode.EVEN
val billOwerIds = bill.billOwersIds
val selectedCount = billOwerIds.size
val selectedMembers = members.filter { billOwerIds.contains(it.id) }
val totalWeight = selectedMembers.sumOf { it.weight }
// Use UI amount for even split calculation
val uiAmount = amountAsDouble
val evenSplit = if (selectedCount > 0) uiAmount / selectedCount else 0.0
val evenSplitStr = if (evenSplit == 0.0) "" else SupportUtil.round2(evenSplit).toString()
for (member in members) {
val selected = billOwerIds.contains(member.id)
owersSelection[member.id] = selected
if (selected) {
owersCustomSplit[member.id] = evenSplitStr
val share = if (totalWeight > 0) (uiAmount * member.weight) / totalWeight else 0.0
owersCustomSplit[member.id] = if (share == 0.0) "" else SupportUtil.round2(share).toString()
}
}
}
@@ -114,7 +114,8 @@ class ManageCurrenciesActivity : AppCompatActivity() {
}
private fun addOrUpdateCurrency() {
val exchangeRate = try { viewModel.newCurrencyRate.toDouble() } catch (_: Exception) { 0.0 }
val uiRate = try { viewModel.newCurrencyRate.toDouble() } catch (_: Exception) { 0.0 }
val exchangeRate = if (uiRate != 0.0) 1.0 / uiRate else 0.0
val currencyName = viewModel.newCurrencyName
val editingId = viewModel.editingCurrencyId
@@ -142,7 +143,8 @@ class ManageCurrenciesActivity : AppCompatActivity() {
private fun startEditing(currency: DBCurrency) {
viewModel.editingCurrencyId = currency.id
viewModel.newCurrencyName = currency.name ?: ""
viewModel.newCurrencyRate = currency.exchangeRate.toString()
val uiRate = if (currency.exchangeRate != 0.0) 1.0 / currency.exchangeRate else 0.0
viewModel.newCurrencyRate = uiRate.toString()
}
private fun cancelEditing() {
@@ -304,7 +304,7 @@ fun CurrencyRow(
modifier = Modifier.padding(horizontal = 8.dp)
)
Text(
text = formatAmount(currency.exchangeRate),
text = formatAmount(if (currency.exchangeRate != 0.0) 1.0 / currency.exchangeRate else 0.0),
style = MaterialTheme.typography.body1,
fontWeight = FontWeight.ExtraBold,
color = if (isEditing) MaterialTheme.colors.secondary else MaterialTheme.colors.onSurface