Fix currency and added user weighting
This commit is contained in:
@@ -333,7 +333,7 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
viewModel.owersSelection[id] == true && viewModel.parseAmountFromUi(amountStr) > 0
|
viewModel.owersSelection[id] == true && viewModel.parseAmountFromUi(amountStr) > 0
|
||||||
}.mapValues {
|
}.mapValues {
|
||||||
val uiAmount = viewModel.parseAmountFromUi(it.value)
|
val uiAmount = viewModel.parseAmountFromUi(it.value)
|
||||||
SupportUtil.round2(uiAmount / viewModel.selectedCurrencyRate)
|
SupportUtil.round2(uiAmount * viewModel.selectedCurrencyRate)
|
||||||
}
|
}
|
||||||
} else { // PERCENT
|
} else { // PERCENT
|
||||||
val totalAmount = viewModel.amountAsDouble
|
val totalAmount = viewModel.amountAsDouble
|
||||||
@@ -342,7 +342,7 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
}.mapValues {
|
}.mapValues {
|
||||||
val percent = viewModel.parseAmountFromUi(it.value)
|
val percent = viewModel.parseAmountFromUi(it.value)
|
||||||
val uiAmount = SupportUtil.round2(totalAmount * percent / 100.0)
|
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
|
size = 32.dp
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(8.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) {
|
if (isSelected || viewModel.splitMode != SplitMode.EVEN) {
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
@@ -671,19 +674,115 @@ fun BillAdditionalDetailsSection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ViewModelConstructorInComposable")
|
@SuppressLint("ViewModelConstructorInComposable")
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true, name = "Standard Split")
|
||||||
@Composable
|
@Composable
|
||||||
fun EditBillScreenPreview() {
|
fun EditBillScreenPreview() {
|
||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
EditBillScreen(
|
EditBillScreen(
|
||||||
viewModel = EditBillViewModel().apply {
|
viewModel = EditBillViewModel().apply {
|
||||||
what = "Pizza"
|
what = "Pizza"
|
||||||
amount = "12.50"
|
amount = "12.00"
|
||||||
mainCurrencyName = "EUR"
|
mainCurrencyName = "EUR"
|
||||||
members = listOf(
|
members = listOf(
|
||||||
DBMember(1, 0, 0, "Alice", true, 1.0, 0, null, null, null, null, null),
|
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)
|
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(),
|
categories = emptyList(),
|
||||||
paymentModes = emptyList(),
|
paymentModes = emptyList(),
|
||||||
|
|||||||
@@ -61,18 +61,18 @@ class EditBillViewModel : ViewModel() {
|
|||||||
val amountAsDouble: Double
|
val amountAsDouble: Double
|
||||||
get() = parseAmountFromUi(amount)
|
get() = parseAmountFromUi(amount)
|
||||||
|
|
||||||
fun getEvenSplit(): Double {
|
val hasDifferentWeights: Boolean
|
||||||
val selectedOwersCount = owersSelection.count { it.value }
|
get() = members.isNotEmpty() && members.any { it.weight != members[0].weight }
|
||||||
return if (selectedOwersCount > 0) SupportUtil.round2(amountAsDouble / selectedOwersCount) else 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateSplits() {
|
fun updateSplits() {
|
||||||
if (splitMode == SplitMode.EVEN) {
|
if (splitMode == SplitMode.EVEN) {
|
||||||
val even = getEvenSplit()
|
val selectedMembers = members.filter { owersSelection[it.id] == true }
|
||||||
val evenStr = if (even == 0.0) "" else SupportUtil.round2(even).toString()
|
val totalWeight = selectedMembers.sumOf { it.weight }
|
||||||
|
val uiAmount = amountAsDouble
|
||||||
members.forEach { m ->
|
members.forEach { m ->
|
||||||
if (owersSelection[m.id] == true) {
|
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 {
|
} else {
|
||||||
owersCustomSplit.remove(m.id)
|
owersCustomSplit.remove(m.id)
|
||||||
}
|
}
|
||||||
@@ -180,7 +180,7 @@ class EditBillViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getFinalAmount(): Double {
|
fun getFinalAmount(): Double {
|
||||||
return SupportUtil.round2(amountAsDouble / selectedCurrencyRate)
|
return SupportUtil.round2(amountAsDouble * selectedCurrencyRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFinalComment(): String {
|
fun getFinalComment(): String {
|
||||||
@@ -252,10 +252,9 @@ class EditBillViewModel : ViewModel() {
|
|||||||
if (selected) {
|
if (selected) {
|
||||||
// If we have metadata, the custom splits from DB are also converted.
|
// If we have metadata, the custom splits from DB are also converted.
|
||||||
// We should show them as "Original" if possible?
|
// We should show them as "Original" if possible?
|
||||||
// Actually, if we use metadata, we should probably store original splits too,
|
// Reverse calculation for display: UI Part = DB Part / Rate
|
||||||
// but for now let's just reverse the rate for display.
|
|
||||||
val dbPart = customSplits[member.id]!!
|
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()
|
owersCustomSplit[member.id] = SupportUtil.round2(uiPart).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,18 +262,18 @@ class EditBillViewModel : ViewModel() {
|
|||||||
// Even split logic
|
// Even split logic
|
||||||
splitMode = SplitMode.EVEN
|
splitMode = SplitMode.EVEN
|
||||||
val billOwerIds = bill.billOwersIds
|
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
|
// Use UI amount for even split calculation
|
||||||
val uiAmount = amountAsDouble
|
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) {
|
for (member in members) {
|
||||||
val selected = billOwerIds.contains(member.id)
|
val selected = billOwerIds.contains(member.id)
|
||||||
owersSelection[member.id] = selected
|
owersSelection[member.id] = selected
|
||||||
if (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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -114,7 +114,8 @@ class ManageCurrenciesActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addOrUpdateCurrency() {
|
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 currencyName = viewModel.newCurrencyName
|
||||||
val editingId = viewModel.editingCurrencyId
|
val editingId = viewModel.editingCurrencyId
|
||||||
|
|
||||||
@@ -142,7 +143,8 @@ class ManageCurrenciesActivity : AppCompatActivity() {
|
|||||||
private fun startEditing(currency: DBCurrency) {
|
private fun startEditing(currency: DBCurrency) {
|
||||||
viewModel.editingCurrencyId = currency.id
|
viewModel.editingCurrencyId = currency.id
|
||||||
viewModel.newCurrencyName = currency.name ?: ""
|
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() {
|
private fun cancelEditing() {
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ fun CurrencyRow(
|
|||||||
modifier = Modifier.padding(horizontal = 8.dp)
|
modifier = Modifier.padding(horizontal = 8.dp)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = formatAmount(currency.exchangeRate),
|
text = formatAmount(if (currency.exchangeRate != 0.0) 1.0 / currency.exchangeRate else 0.0),
|
||||||
style = MaterialTheme.typography.body1,
|
style = MaterialTheme.typography.body1,
|
||||||
fontWeight = FontWeight.ExtraBold,
|
fontWeight = FontWeight.ExtraBold,
|
||||||
color = if (isEditing) MaterialTheme.colors.secondary else MaterialTheme.colors.onSurface
|
color = if (isEditing) MaterialTheme.colors.secondary else MaterialTheme.colors.onSurface
|
||||||
|
|||||||
Reference in New Issue
Block a user