Compare commits
11 Commits
1.1d
...
eec95cdcb0
| Author | SHA1 | Date | |
|---|---|---|---|
| eec95cdcb0 | |||
|
106c4e27fc
|
|||
|
47d0471b48
|
|||
| 56968dbe3b | |||
|
56036d5840
|
|||
|
4fa29da79d
|
|||
| c2c1017633 | |||
| 3aeabf4dd3 | |||
| 937c3db1f5 | |||
| 5b3c47045c | |||
| c2f59fa1df |
+1
-1
@@ -14,7 +14,7 @@ android {
|
|||||||
applicationId "net.helcel.cowspent"
|
applicationId "net.helcel.cowspent"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 37
|
targetSdk = 37
|
||||||
versionName project.hasProperty('VERSION_NAME') ? project.property('VERSION_NAME') : "1"
|
versionName project.hasProperty('VERSION_NAME') ? project.property('VERSION_NAME') : "1.4"
|
||||||
versionCode project.hasProperty('VERSION_CODE') ? project.property('VERSION_CODE').toInteger() : 1
|
versionCode project.hasProperty('VERSION_CODE') ? project.property('VERSION_CODE').toInteger() : 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import net.helcel.cowspent.model.ProjectType
|
|||||||
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
|
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
|
||||||
import net.helcel.cowspent.theme.ThemeUtils
|
import net.helcel.cowspent.theme.ThemeUtils
|
||||||
import net.helcel.cowspent.util.BillParser
|
import net.helcel.cowspent.util.BillParser
|
||||||
import net.helcel.cowspent.util.CategoryUtils
|
|
||||||
import net.helcel.cowspent.util.SupportUtil
|
import net.helcel.cowspent.util.SupportUtil
|
||||||
import java.text.ParseException
|
import java.text.ParseException
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
@@ -52,23 +51,10 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
ThemeUtils.CowspentTheme {
|
ThemeUtils.CowspentTheme {
|
||||||
|
|
||||||
val categories = remember {
|
val categories = remember {
|
||||||
val syncedCategories = db.getCategories(bill.projectId)
|
db.getCategories(bill.projectId)
|
||||||
val defaultCategories = CategoryUtils.getDefaultCategories(this@EditBillActivity, bill.projectId)
|
|
||||||
val hardcoded = if (projectType == ProjectType.LOCAL) {
|
|
||||||
defaultCategories
|
|
||||||
} else {
|
|
||||||
listOfNotNull(defaultCategories.find { it.remoteId.toInt() == DBBill.CATEGORY_REIMBURSEMENT })
|
|
||||||
}
|
|
||||||
syncedCategories + hardcoded
|
|
||||||
}
|
}
|
||||||
val paymentModes = remember {
|
val paymentModes = remember {
|
||||||
val syncedPaymentModes = db.getPaymentModes(bill.projectId)
|
db.getPaymentModes(bill.projectId)
|
||||||
val defaultPaymentModes = CategoryUtils.getDefaultPaymentModes(this@EditBillActivity, bill.projectId)
|
|
||||||
if (projectType == ProjectType.LOCAL) {
|
|
||||||
syncedPaymentModes + defaultPaymentModes
|
|
||||||
} else {
|
|
||||||
syncedPaymentModes.ifEmpty { defaultPaymentModes }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EditBillScreen(
|
EditBillScreen(
|
||||||
@@ -144,7 +130,7 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
bill = DBBill(
|
bill = DBBill(
|
||||||
first.id, 0, first.projectId, first.payerId, totalAmount,
|
first.id, 0, first.projectId, first.payerId, totalAmount,
|
||||||
first.timestamp, first.what, first.state, first.repeat,
|
first.timestamp, first.what, first.state, first.repeat,
|
||||||
first.paymentMode, first.categoryRemoteId, first.comment, first.paymentModeRemoteId
|
first.paymentMode, first.categoryId, first.comment, first.paymentModeId
|
||||||
)
|
)
|
||||||
|
|
||||||
val splits = mutableMapOf<Long, Double>()
|
val splits = mutableMapOf<Long, Double>()
|
||||||
@@ -175,8 +161,8 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
bill = DBBill(
|
bill = DBBill(
|
||||||
0, 0, projectId, btd.payerId, btd.amount,
|
0, 0, projectId, btd.payerId, btd.amount,
|
||||||
timeNowSeconds, btd.what, DBBill.STATE_ADDED,
|
timeNowSeconds, btd.what, DBBill.STATE_ADDED,
|
||||||
btd.repeat, btd.paymentMode, btd.categoryRemoteId,
|
btd.repeat, btd.paymentMode, btd.categoryId,
|
||||||
btd.comment, btd.paymentModeRemoteId
|
btd.comment, btd.paymentModeId
|
||||||
)
|
)
|
||||||
val btdOwers = btd.billOwers
|
val btdOwers = btd.billOwers
|
||||||
val newBillOwers = btdOwers.filter {
|
val newBillOwers = btdOwers.filter {
|
||||||
@@ -195,6 +181,11 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
val project = db.getProject(bill.projectId)
|
val project = db.getProject(bill.projectId)
|
||||||
val currencies = db.getCurrencies(bill.projectId)
|
val currencies = db.getCurrencies(bill.projectId)
|
||||||
|
|
||||||
|
if (project != null) {
|
||||||
|
db.ensureDefaultLabels(bill.projectId, project.type)
|
||||||
|
}
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
viewModel.currencies = currencies
|
viewModel.currencies = currencies
|
||||||
viewModel.mainCurrencyName = project?.currencyName ?: ""
|
viewModel.mainCurrencyName = project?.currencyName ?: ""
|
||||||
@@ -239,8 +230,8 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
bill = DBBill(
|
bill = DBBill(
|
||||||
0, 0, bill.projectId, viewModel.payerId, viewModel.amountAsDouble,
|
0, 0, bill.projectId, viewModel.payerId, viewModel.amountAsDouble,
|
||||||
System.currentTimeMillis() / 1000, viewModel.what, DBBill.STATE_ADDED,
|
System.currentTimeMillis() / 1000, viewModel.what, DBBill.STATE_ADDED,
|
||||||
viewModel.repeat, bill.paymentMode, viewModel.categoryRemoteId,
|
viewModel.repeat, bill.paymentMode, viewModel.categoryId,
|
||||||
viewModel.getFinalComment(), viewModel.paymentModeRemoteId
|
viewModel.getFinalComment(), viewModel.paymentModeId
|
||||||
)
|
)
|
||||||
calendar.timeInMillis = System.currentTimeMillis()
|
calendar.timeInMillis = System.currentTimeMillis()
|
||||||
viewModel.timestamp = calendar.timeInMillis / 1000
|
viewModel.timestamp = calendar.timeInMillis / 1000
|
||||||
@@ -326,8 +317,8 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
bill.payerId == viewModel.payerId &&
|
bill.payerId == viewModel.payerId &&
|
||||||
bill.comment == viewModel.getFinalComment() &&
|
bill.comment == viewModel.getFinalComment() &&
|
||||||
bill.repeat == viewModel.repeat &&
|
bill.repeat == viewModel.repeat &&
|
||||||
bill.categoryRemoteId == viewModel.categoryRemoteId &&
|
bill.categoryId == viewModel.categoryId &&
|
||||||
bill.paymentModeRemoteId == viewModel.paymentModeRemoteId &&
|
bill.paymentModeId == viewModel.paymentModeId &&
|
||||||
!owersChanged)
|
!owersChanged)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,8 +377,8 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
listOf(memberId),
|
listOf(memberId),
|
||||||
viewModel.repeat,
|
viewModel.repeat,
|
||||||
existingBill.paymentMode,
|
existingBill.paymentMode,
|
||||||
viewModel.paymentModeRemoteId,
|
viewModel.paymentModeId,
|
||||||
viewModel.categoryRemoteId,
|
viewModel.categoryId,
|
||||||
finalComment
|
finalComment
|
||||||
)
|
)
|
||||||
if (firstSavedId == 0L) firstSavedId = billToUseId
|
if (firstSavedId == 0L) firstSavedId = billToUseId
|
||||||
@@ -396,9 +387,11 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
val newBill = DBBill(
|
val newBill = DBBill(
|
||||||
0, 0, bill.projectId, viewModel.payerId, amount,
|
0, 0, bill.projectId, viewModel.payerId, amount,
|
||||||
viewModel.timestamp, viewModel.what, DBBill.STATE_ADDED, viewModel.repeat,
|
viewModel.timestamp, viewModel.what, DBBill.STATE_ADDED, viewModel.repeat,
|
||||||
bill.paymentMode, viewModel.categoryRemoteId, finalComment, viewModel.paymentModeRemoteId
|
bill.paymentMode, viewModel.categoryId, finalComment, viewModel.paymentModeId
|
||||||
)
|
)
|
||||||
newBill.billOwers = listOf(DBBillOwer(0, 0, memberId))
|
newBill.billOwers = listOf(DBBillOwer(0, 0, memberId))
|
||||||
|
newBill.categoryId = viewModel.categoryId
|
||||||
|
newBill.paymentModeId = viewModel.paymentModeId
|
||||||
val newId = db.addBill(newBill)
|
val newId = db.addBill(newBill)
|
||||||
if (firstSavedId == 0L) firstSavedId = newId
|
if (firstSavedId == 0L) firstSavedId = newId
|
||||||
}
|
}
|
||||||
@@ -441,8 +434,8 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
newOwersIds,
|
newOwersIds,
|
||||||
viewModel.repeat,
|
viewModel.repeat,
|
||||||
bill.paymentMode,
|
bill.paymentMode,
|
||||||
viewModel.paymentModeRemoteId,
|
viewModel.paymentModeId,
|
||||||
viewModel.categoryRemoteId,
|
viewModel.categoryId,
|
||||||
finalComment
|
finalComment
|
||||||
)
|
)
|
||||||
if (groupedBillIds != null) {
|
if (groupedBillIds != null) {
|
||||||
@@ -460,7 +453,7 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
val newBill = DBBill(
|
val newBill = DBBill(
|
||||||
0, 0, bill.projectId, viewModel.payerId, newAmount,
|
0, 0, bill.projectId, viewModel.payerId, newAmount,
|
||||||
viewModel.timestamp, viewModel.what, DBBill.STATE_ADDED, viewModel.repeat,
|
viewModel.timestamp, viewModel.what, DBBill.STATE_ADDED, viewModel.repeat,
|
||||||
bill.paymentMode, viewModel.categoryRemoteId, finalComment, viewModel.paymentModeRemoteId
|
bill.paymentMode, viewModel.categoryId, finalComment, viewModel.paymentModeId
|
||||||
)
|
)
|
||||||
newOwersIds.forEach { newBill.billOwers += DBBillOwer(0, 0, it) }
|
newOwersIds.forEach { newBill.billOwers += DBBillOwer(0, 0, it) }
|
||||||
val newBillId = db.addBill(newBill)
|
val newBillId = db.addBill(newBill)
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import androidx.compose.ui.unit.sp
|
|||||||
import net.helcel.cowspent.R
|
import net.helcel.cowspent.R
|
||||||
import net.helcel.cowspent.android.helper.*
|
import net.helcel.cowspent.android.helper.*
|
||||||
import net.helcel.cowspent.model.*
|
import net.helcel.cowspent.model.*
|
||||||
|
import net.helcel.cowspent.util.CategoryUtils
|
||||||
import net.helcel.cowspent.util.SupportUtil
|
import net.helcel.cowspent.util.SupportUtil
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
@@ -466,9 +467,10 @@ fun BillAdditionalDetailsSection(
|
|||||||
modifier = Modifier.padding(bottom = 8.dp, top = 16.dp)
|
modifier = Modifier.padding(bottom = 8.dp, top = 16.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
var categoryExpanded by remember { mutableStateOf(false) }
|
var categoryExpanded by remember { mutableStateOf(false) }
|
||||||
val selectedCategory =
|
val selectedCategory =
|
||||||
categories.find { it.remoteId.toInt() == viewModel.categoryRemoteId }
|
categories.find { it.id == viewModel.categoryId } ?: CategoryUtils.getCategoryById(context, viewModel.categoryId)
|
||||||
|
|
||||||
EditableExposedDropdownMenu(
|
EditableExposedDropdownMenu(
|
||||||
value = selectedCategory?.name ?: "",
|
value = selectedCategory?.name ?: "",
|
||||||
@@ -488,16 +490,26 @@ fun BillAdditionalDetailsSection(
|
|||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
DropdownMenuItem(onClick = {
|
DropdownMenuItem(onClick = {
|
||||||
viewModel.categoryRemoteId = 0
|
viewModel.categoryId = 0
|
||||||
categoryExpanded = false
|
categoryExpanded = false
|
||||||
}) {
|
}) {
|
||||||
Icon(Icons.Default.Close, tint = Color.Red, contentDescription = null)
|
Icon(Icons.Default.Close, tint = Color.Red, contentDescription = null)
|
||||||
Spacer(modifier = Modifier.width(12.dp))
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
Text(stringResource(R.string.category_none))
|
Text(stringResource(R.string.category_none))
|
||||||
}
|
}
|
||||||
categories.forEach { category ->
|
|
||||||
|
DropdownMenuItem(onClick = {
|
||||||
|
viewModel.categoryId = DBBill.CATEGORY_REIMBURSEMENT
|
||||||
|
categoryExpanded = false
|
||||||
|
}) {
|
||||||
|
Text(text = "\uD83D\uDCB0", fontSize = 20.sp)
|
||||||
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
|
Text(stringResource(R.string.category_reimbursement))
|
||||||
|
}
|
||||||
|
|
||||||
|
categories.filter { it.remoteId != DBBill.CATEGORY_REIMBURSEMENT }.forEach { category ->
|
||||||
DropdownMenuItem(onClick = {
|
DropdownMenuItem(onClick = {
|
||||||
viewModel.categoryRemoteId = category.remoteId.toInt()
|
viewModel.categoryId = category.id
|
||||||
categoryExpanded = false
|
categoryExpanded = false
|
||||||
}) {
|
}) {
|
||||||
Text(text = category.icon, fontSize = 20.sp)
|
Text(text = category.icon, fontSize = 20.sp)
|
||||||
@@ -512,7 +524,7 @@ fun BillAdditionalDetailsSection(
|
|||||||
|
|
||||||
var pmExpanded by remember { mutableStateOf(false) }
|
var pmExpanded by remember { mutableStateOf(false) }
|
||||||
val selectedPm =
|
val selectedPm =
|
||||||
paymentModes.find { it.remoteId.toInt() == viewModel.paymentModeRemoteId }
|
paymentModes.find { it.id == viewModel.paymentModeId } ?: CategoryUtils.getPaymentModeById(context, viewModel.paymentModeId)
|
||||||
|
|
||||||
EditableExposedDropdownMenu(
|
EditableExposedDropdownMenu(
|
||||||
value = selectedPm?.name ?: "",
|
value = selectedPm?.name ?: "",
|
||||||
@@ -532,7 +544,7 @@ fun BillAdditionalDetailsSection(
|
|||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
DropdownMenuItem(onClick = {
|
DropdownMenuItem(onClick = {
|
||||||
viewModel.paymentModeRemoteId = 0
|
viewModel.paymentModeId = 0
|
||||||
pmExpanded = false
|
pmExpanded = false
|
||||||
}) {
|
}) {
|
||||||
Icon(Icons.Default.Close, tint = Color.Red, contentDescription = null)
|
Icon(Icons.Default.Close, tint = Color.Red, contentDescription = null)
|
||||||
@@ -541,7 +553,7 @@ fun BillAdditionalDetailsSection(
|
|||||||
}
|
}
|
||||||
paymentModes.forEach { pm ->
|
paymentModes.forEach { pm ->
|
||||||
DropdownMenuItem(onClick = {
|
DropdownMenuItem(onClick = {
|
||||||
viewModel.paymentModeRemoteId = pm.remoteId.toInt()
|
viewModel.paymentModeId = pm.id
|
||||||
pmExpanded = false
|
pmExpanded = false
|
||||||
}) {
|
}) {
|
||||||
Text(text = pm.icon, fontSize = 20.sp)
|
Text(text = pm.icon, fontSize = 20.sp)
|
||||||
|
|||||||
@@ -2,22 +2,20 @@ package net.helcel.cowspent.android.bill_edit
|
|||||||
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableDoubleStateOf
|
import androidx.compose.runtime.mutableDoubleStateOf
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
|
||||||
import androidx.compose.runtime.mutableLongStateOf
|
import androidx.compose.runtime.mutableLongStateOf
|
||||||
import androidx.compose.runtime.mutableStateMapOf
|
import androidx.compose.runtime.mutableStateMapOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import net.helcel.cowspent.model.DBBill
|
|
||||||
import net.helcel.cowspent.model.DBMember
|
|
||||||
import net.helcel.cowspent.android.helper.DialogState
|
import net.helcel.cowspent.android.helper.DialogState
|
||||||
import net.helcel.cowspent.android.helper.parseAmount
|
import net.helcel.cowspent.android.helper.parseAmount
|
||||||
|
import net.helcel.cowspent.model.DBBill
|
||||||
|
import net.helcel.cowspent.model.DBCurrency
|
||||||
|
import net.helcel.cowspent.model.DBMember
|
||||||
import net.helcel.cowspent.util.SupportUtil
|
import net.helcel.cowspent.util.SupportUtil
|
||||||
import net.helcel.cowspent.util.evalMath
|
import net.helcel.cowspent.util.evalMath
|
||||||
|
|
||||||
import net.helcel.cowspent.model.DBCurrency
|
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
|
||||||
|
|
||||||
class EditBillViewModel : ViewModel() {
|
class EditBillViewModel : ViewModel() {
|
||||||
var what by mutableStateOf("")
|
var what by mutableStateOf("")
|
||||||
var amount by mutableStateOf("")
|
var amount by mutableStateOf("")
|
||||||
@@ -25,8 +23,8 @@ class EditBillViewModel : ViewModel() {
|
|||||||
var timestamp by mutableLongStateOf(0L)
|
var timestamp by mutableLongStateOf(0L)
|
||||||
var payerId by mutableLongStateOf(0L)
|
var payerId by mutableLongStateOf(0L)
|
||||||
var repeat by mutableStateOf(DBBill.NON_REPEATED)
|
var repeat by mutableStateOf(DBBill.NON_REPEATED)
|
||||||
var paymentModeRemoteId by mutableIntStateOf(0)
|
var paymentModeId by mutableLongStateOf(0L)
|
||||||
var categoryRemoteId by mutableIntStateOf(0)
|
var categoryId by mutableLongStateOf(0L)
|
||||||
var isNewBill by mutableStateOf(false)
|
var isNewBill by mutableStateOf(false)
|
||||||
|
|
||||||
var currencies by mutableStateOf<List<DBCurrency>>(emptyList())
|
var currencies by mutableStateOf<List<DBCurrency>>(emptyList())
|
||||||
@@ -166,8 +164,8 @@ class EditBillViewModel : ViewModel() {
|
|||||||
timestamp = bill.timestamp
|
timestamp = bill.timestamp
|
||||||
payerId = bill.payerId
|
payerId = bill.payerId
|
||||||
repeat = bill.repeat ?: DBBill.NON_REPEATED
|
repeat = bill.repeat ?: DBBill.NON_REPEATED
|
||||||
paymentModeRemoteId = bill.paymentModeRemoteId
|
paymentModeId = bill.paymentModeId
|
||||||
categoryRemoteId = bill.categoryRemoteId
|
categoryId = bill.categoryId
|
||||||
|
|
||||||
val rawComment = bill.comment ?: ""
|
val rawComment = bill.comment ?: ""
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
|
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
|
||||||
import net.helcel.cowspent.theme.ThemeUtils
|
import net.helcel.cowspent.theme.ThemeUtils
|
||||||
import net.helcel.cowspent.util.CategoryUtils
|
|
||||||
import net.helcel.cowspent.model.DBBill
|
import net.helcel.cowspent.model.DBBill
|
||||||
import net.helcel.cowspent.model.ProjectType
|
import net.helcel.cowspent.model.ProjectType
|
||||||
|
|
||||||
@@ -39,24 +38,20 @@ class LabelBillsActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
val members = db.getMembersOfProject(projectId, null)
|
val members = db.getMembersOfProject(projectId, null)
|
||||||
val allBills = db.getBillsOfProject(projectId)
|
val allBills = db.getBillsOfProject(projectId)
|
||||||
val billsToLabel = allBills.filter { it.categoryRemoteId == 0 && it.state != DBBill.STATE_DELETED }
|
val billsToLabel = allBills.filter { it.categoryId == 0L && it.state != DBBill.STATE_DELETED }
|
||||||
val allCategorized = allBills.filter { it.categoryRemoteId != 0 && it.state != DBBill.STATE_DELETED }
|
val allCategorized = allBills.filter { it.categoryId != 0L && it.state != DBBill.STATE_DELETED }
|
||||||
|
|
||||||
val syncedCategories = db.getCategories(projectId)
|
db.ensureDefaultLabels(projectId, projectType)
|
||||||
val defaultCategories = CategoryUtils.getDefaultCategories(this@LabelBillsActivity, projectId)
|
|
||||||
val hardcoded = if (projectType == ProjectType.LOCAL) {
|
// Reload from DB to get real IDs
|
||||||
defaultCategories
|
val categories = db.getCategories(projectId)
|
||||||
} else {
|
|
||||||
listOfNotNull(defaultCategories.find { it.remoteId.toInt() == DBBill.CATEGORY_REIMBURSEMENT })
|
|
||||||
}
|
|
||||||
val categories = syncedCategories + hardcoded
|
|
||||||
|
|
||||||
Quadruple(members, billsToLabel, categories, allCategorized)
|
Quadruple(members, billsToLabel, categories, allCategorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.billsToLabel = billsToLabel
|
viewModel.billsToLabel = billsToLabel
|
||||||
viewModel.categories = categories
|
viewModel.categories = categories
|
||||||
viewModel.categoriesMap = categories.associateBy { it.remoteId }
|
viewModel.categoriesMap = categories.associateBy { it.id }
|
||||||
viewModel.allCategorizedBills = allCategorized
|
viewModel.allCategorizedBills = allCategorized
|
||||||
viewModel.updateSuggestions()
|
viewModel.updateSuggestions()
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ fun LabelBillsScreen(
|
|||||||
CategoryButton(
|
CategoryButton(
|
||||||
icon = category.icon,
|
icon = category.icon,
|
||||||
name = category.name ?: "",
|
name = category.name ?: "",
|
||||||
onClick = { viewModel.labelCurrentBill(db, category.remoteId.toInt()) }
|
onClick = { viewModel.labelCurrentBill(db, category.id) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ fun LabelBillsScreen(
|
|||||||
CategoryButton(
|
CategoryButton(
|
||||||
icon = category.icon,
|
icon = category.icon,
|
||||||
name = category.name ?: "",
|
name = category.name ?: "",
|
||||||
onClick = { viewModel.labelCurrentBill(db, category.remoteId.toInt()) }
|
onClick = { viewModel.labelCurrentBill(db, category.id) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,16 +245,16 @@ fun CategoryButton(icon: String, name: String, onClick: () -> Unit) {
|
|||||||
fun LabelBillsScreenPreview() {
|
fun LabelBillsScreenPreview() {
|
||||||
val viewModel = LabelBillsViewModel().apply {
|
val viewModel = LabelBillsViewModel().apply {
|
||||||
billsToLabel = listOf(
|
billsToLabel = listOf(
|
||||||
DBBill(1L, 0, 1L, 1L, 120.5, System.currentTimeMillis() / 1000, "Groceries at Aldi", 0, null, null, 0, null, -1)
|
DBBill(1L, 0, 1L, 1L, 120.5, System.currentTimeMillis() / 1000, "Groceries at Aldi", 0, null, null, 0L, null, -1L)
|
||||||
)
|
)
|
||||||
val cats = listOf(
|
val cats = listOf(
|
||||||
DBCategory(1, 1, 1, "Groceries", "🛒", ""),
|
DBCategory(1L, 1L, 1L, "Groceries", "🛒", ""),
|
||||||
DBCategory(2, 2, 1, "Leisure", "🥳", ""),
|
DBCategory(2L, 2L, 1L, "Leisure", "🥳", ""),
|
||||||
DBCategory(3, 3, 1, "Rent", "🏠", ""),
|
DBCategory(3L, 3L, 1L, "Rent", "🏠", ""),
|
||||||
DBCategory(4, 4, 1, "Bills", "💸", "")
|
DBCategory(4L, 4L, 1L, "Bills", "💸", "")
|
||||||
)
|
)
|
||||||
categories = cats
|
categories = cats
|
||||||
categoriesMap = cats.associateBy { it.remoteId }
|
categoriesMap = cats.associateBy { it.id }
|
||||||
}
|
}
|
||||||
val members = listOf(
|
val members = listOf(
|
||||||
DBMember(1L, 0, 1L, "Alice", true, 1.0, 0, 255, 100, 100, null, null),
|
DBMember(1L, 0, 1L, "Alice", true, 1.0, 0, 255, 100, 100, null, null),
|
||||||
|
|||||||
@@ -43,18 +43,18 @@ class LabelBillsViewModel : ViewModel() {
|
|||||||
otherName == name || (name.length > 3 && otherName.contains(name)) || (otherName.length > 3 && name.contains(otherName))
|
otherName == name || (name.length > 3 && otherName.contains(name)) || (otherName.length > 3 && name.contains(otherName))
|
||||||
}
|
}
|
||||||
|
|
||||||
val counts = matches.groupBy { it.categoryRemoteId }
|
val counts = matches.groupBy { it.categoryId }
|
||||||
.mapValues { it.value.size }
|
.mapValues { it.value.size }
|
||||||
.toList()
|
.toList()
|
||||||
.sortedByDescending { it.second }
|
.sortedByDescending { it.second }
|
||||||
.take(2)
|
.take(2)
|
||||||
|
|
||||||
suggestedCategories = counts.mapNotNull { (catId, _) ->
|
suggestedCategories = counts.mapNotNull { (catId, _) ->
|
||||||
categoriesMap[catId.toLong()]
|
categoriesMap[catId]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun labelCurrentBill(db: CowspentSQLiteOpenHelper, categoryId: Int) {
|
fun labelCurrentBill(db: CowspentSQLiteOpenHelper, categoryId: Long) {
|
||||||
currentBill?.let { bill ->
|
currentBill?.let { bill ->
|
||||||
db.updateBillAndSync(
|
db.updateBillAndSync(
|
||||||
bill = bill,
|
bill = bill,
|
||||||
@@ -65,11 +65,11 @@ class LabelBillsViewModel : ViewModel() {
|
|||||||
newOwersIds = bill.billOwersIds,
|
newOwersIds = bill.billOwersIds,
|
||||||
newRepeat = bill.repeat,
|
newRepeat = bill.repeat,
|
||||||
newPaymentMode = bill.paymentMode,
|
newPaymentMode = bill.paymentMode,
|
||||||
newPaymentModeRemoteId = bill.paymentModeRemoteId,
|
newPaymentModeId = bill.paymentModeId,
|
||||||
newCategoryId = categoryId,
|
newCategoryId = categoryId,
|
||||||
newComment = bill.comment
|
newComment = bill.comment
|
||||||
)
|
)
|
||||||
bill.categoryRemoteId = categoryId
|
bill.categoryId = categoryId
|
||||||
onBillProcessed?.invoke(bill.id)
|
onBillProcessed?.invoke(bill.id)
|
||||||
moveToNext()
|
moveToNext()
|
||||||
}
|
}
|
||||||
@@ -87,11 +87,11 @@ class LabelBillsViewModel : ViewModel() {
|
|||||||
|
|
||||||
val start = currentBillIndex
|
val start = currentBillIndex
|
||||||
var next = (start + 1) % billsToLabel.size
|
var next = (start + 1) % billsToLabel.size
|
||||||
while (next != start && billsToLabel[next].categoryRemoteId != 0) {
|
while (next != start && billsToLabel[next].categoryId != 0L) {
|
||||||
next = (next + 1) % billsToLabel.size
|
next = (next + 1) % billsToLabel.size
|
||||||
}
|
}
|
||||||
|
|
||||||
currentBillIndex = if (billsToLabel[next].categoryRemoteId == 0) {
|
currentBillIndex = if (billsToLabel[next].categoryId == 0L) {
|
||||||
next
|
next
|
||||||
} else {
|
} else {
|
||||||
billsToLabel.size
|
billsToLabel.size
|
||||||
|
|||||||
+5
-3
@@ -33,9 +33,11 @@ class ManageCurrenciesActivity : AppCompatActivity() {
|
|||||||
if (message.isEmpty()) {
|
if (message.isEmpty()) {
|
||||||
showToast(this@ManageCurrenciesActivity,getString(R.string.currency_saved_success), Toast.LENGTH_LONG)
|
showToast(this@ManageCurrenciesActivity,getString(R.string.currency_saved_success), Toast.LENGTH_LONG)
|
||||||
} else {
|
} else {
|
||||||
viewModel.showDialog(title=getString(R.string.error_edit_remote_project_helper, message),
|
viewModel.showDialog(
|
||||||
message=getString(R.string.action_currencies),
|
title = getString(R.string.dialog_sync_error_title),
|
||||||
positiveText = getString(android.R.string.ok))
|
message = getString(R.string.error_edit_remote_project_helper, message),
|
||||||
|
positiveText = getString(android.R.string.ok)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun onScheduled() {}
|
override fun onScheduled() {}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import androidx.core.graphics.toColorInt
|
|||||||
import net.helcel.cowspent.R
|
import net.helcel.cowspent.R
|
||||||
import net.helcel.cowspent.android.helper.ColorPicker
|
import net.helcel.cowspent.android.helper.ColorPicker
|
||||||
import net.helcel.cowspent.android.helper.StatefulAlertDialog
|
import net.helcel.cowspent.android.helper.StatefulAlertDialog
|
||||||
|
import net.helcel.cowspent.model.DBBill
|
||||||
import net.helcel.cowspent.model.DBCategory
|
import net.helcel.cowspent.model.DBCategory
|
||||||
import net.helcel.cowspent.model.DBPaymentMode
|
import net.helcel.cowspent.model.DBPaymentMode
|
||||||
|
|
||||||
@@ -59,10 +60,10 @@ fun LabelManagementScreenContent(
|
|||||||
dialogState: net.helcel.cowspent.android.helper.DialogState?,
|
dialogState: net.helcel.cowspent.android.helper.DialogState?,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onAddCategory: (String, String, String) -> Unit,
|
onAddCategory: (String, String, String) -> Unit,
|
||||||
onUpdateCategory: (Long, String, String, String) -> Unit,
|
onUpdateCategory: (DBCategory, String, String, String) -> Unit,
|
||||||
onDeleteCategory: (Long) -> Unit,
|
onDeleteCategory: (Long) -> Unit,
|
||||||
onAddPaymentMode: (String, String, String) -> Unit,
|
onAddPaymentMode: (String, String, String) -> Unit,
|
||||||
onUpdatePaymentMode: (Long, String, String, String) -> Unit,
|
onUpdatePaymentMode: (DBPaymentMode, String, String, String) -> Unit,
|
||||||
onDeletePaymentMode: (Long) -> Unit,
|
onDeletePaymentMode: (Long) -> Unit,
|
||||||
onDismissDialog: () -> Unit,
|
onDismissDialog: () -> Unit,
|
||||||
onShowDialog: (net.helcel.cowspent.android.helper.DialogState) -> Unit,
|
onShowDialog: (net.helcel.cowspent.android.helper.DialogState) -> Unit,
|
||||||
@@ -123,7 +124,7 @@ fun LabelManagementScreenContent(
|
|||||||
|
|
||||||
if (selectedTab == 0) {
|
if (selectedTab == 0) {
|
||||||
CategoryList(
|
CategoryList(
|
||||||
categories = categories,
|
categories = categories.filter { it.remoteId != DBBill.CATEGORY_REIMBURSEMENT },
|
||||||
onEdit = {
|
onEdit = {
|
||||||
editingCategory = it
|
editingCategory = it
|
||||||
showEditDialog = true
|
showEditDialog = true
|
||||||
@@ -162,7 +163,7 @@ fun LabelManagementScreenContent(
|
|||||||
if (showEditDialog) {
|
if (showEditDialog) {
|
||||||
if (selectedTab == 0) {
|
if (selectedTab == 0) {
|
||||||
EditLabelDialog(
|
EditLabelDialog(
|
||||||
title = if (editingCategory == null) stringResource(R.string.action_add_project) else stringResource(R.string.action_edit),
|
title = if (editingCategory == null) stringResource(R.string.title_add_category) else stringResource(R.string.action_edit),
|
||||||
initialName = editingCategory?.name ?: "",
|
initialName = editingCategory?.name ?: "",
|
||||||
initialIcon = editingCategory?.icon ?: "",
|
initialIcon = editingCategory?.icon ?: "",
|
||||||
initialColor = editingCategory?.color ?: "#FF0000",
|
initialColor = editingCategory?.color ?: "#FF0000",
|
||||||
@@ -171,14 +172,14 @@ fun LabelManagementScreenContent(
|
|||||||
if (editingCategory == null) {
|
if (editingCategory == null) {
|
||||||
onAddCategory(name, icon, color)
|
onAddCategory(name, icon, color)
|
||||||
} else {
|
} else {
|
||||||
onUpdateCategory(editingCategory!!.id, name, icon, color)
|
onUpdateCategory(editingCategory!!, name, icon, color)
|
||||||
}
|
}
|
||||||
showEditDialog = false
|
showEditDialog = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
EditLabelDialog(
|
EditLabelDialog(
|
||||||
title = if (editingPaymentMode == null) stringResource(R.string.action_add_project) else stringResource(R.string.action_edit),
|
title = if (editingPaymentMode == null) stringResource(R.string.title_add_payment_mode) else stringResource(R.string.action_edit),
|
||||||
initialName = editingPaymentMode?.name ?: "",
|
initialName = editingPaymentMode?.name ?: "",
|
||||||
initialIcon = editingPaymentMode?.icon ?: "",
|
initialIcon = editingPaymentMode?.icon ?: "",
|
||||||
initialColor = editingPaymentMode?.color ?: "#00FF00",
|
initialColor = editingPaymentMode?.color ?: "#00FF00",
|
||||||
@@ -187,7 +188,7 @@ fun LabelManagementScreenContent(
|
|||||||
if (editingPaymentMode == null) {
|
if (editingPaymentMode == null) {
|
||||||
onAddPaymentMode(name, icon, color)
|
onAddPaymentMode(name, icon, color)
|
||||||
} else {
|
} else {
|
||||||
onUpdatePaymentMode(editingPaymentMode!!.id, name, icon, color)
|
onUpdatePaymentMode(editingPaymentMode!!, name, icon, color)
|
||||||
}
|
}
|
||||||
showEditDialog = false
|
showEditDialog = false
|
||||||
}
|
}
|
||||||
@@ -353,8 +354,8 @@ fun LabelManagementCategoriesPreview() {
|
|||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
LabelManagementScreenContent(
|
LabelManagementScreenContent(
|
||||||
categories = listOf(
|
categories = listOf(
|
||||||
DBCategory(1, 1, 1, "Groceries", "🛒", "#FF0000"),
|
DBCategory(1L, 1L, 1L, "Groceries", "🛒", "#FF0000"),
|
||||||
DBCategory(2, 2, 1, "Rent", "🏠", "#00FF00")
|
DBCategory(2L, 2L, 1L, "Rent", "🏠", "#00FF00")
|
||||||
),
|
),
|
||||||
paymentModes = emptyList(),
|
paymentModes = emptyList(),
|
||||||
dialogState = null,
|
dialogState = null,
|
||||||
@@ -379,8 +380,8 @@ fun LabelManagementPaymentModesPreview() {
|
|||||||
LabelManagementScreenContent(
|
LabelManagementScreenContent(
|
||||||
categories = emptyList(),
|
categories = emptyList(),
|
||||||
paymentModes = listOf(
|
paymentModes = listOf(
|
||||||
DBPaymentMode(1, 1, 1, "Cash", "💵", "#0000FF"),
|
DBPaymentMode(1L, 1L, 1L, "Cash", "💵", "#0000FF"),
|
||||||
DBPaymentMode(2, 2, 1, "Credit Card", "💳", "#FFFF00")
|
DBPaymentMode(2L, 2L, 1L, "Credit Card", "💳", "#FFFF00")
|
||||||
),
|
),
|
||||||
dialogState = null,
|
dialogState = null,
|
||||||
onBack = {},
|
onBack = {},
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import kotlinx.coroutines.withContext
|
|||||||
import net.helcel.cowspent.android.helper.DialogState
|
import net.helcel.cowspent.android.helper.DialogState
|
||||||
import net.helcel.cowspent.model.DBCategory
|
import net.helcel.cowspent.model.DBCategory
|
||||||
import net.helcel.cowspent.model.DBPaymentMode
|
import net.helcel.cowspent.model.DBPaymentMode
|
||||||
|
import net.helcel.cowspent.model.DBBill
|
||||||
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
|
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
|
||||||
import net.helcel.cowspent.util.CategoryUtils
|
|
||||||
|
|
||||||
class LabelManagementViewModel(application: Application) : AndroidViewModel(application) {
|
class LabelManagementViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
private val db = CowspentSQLiteOpenHelper.getInstance(application)
|
private val db = CowspentSQLiteOpenHelper.getInstance(application)
|
||||||
@@ -28,44 +28,30 @@ class LabelManagementViewModel(application: Application) : AndroidViewModel(appl
|
|||||||
fun loadLabels(projId: Long) {
|
fun loadLabels(projId: Long) {
|
||||||
projectId = projId
|
projectId = projId
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val cats = withContext(Dispatchers.IO) { db.getCategories(projId) }
|
val project = withContext(Dispatchers.IO) { db.getProject(projId) }
|
||||||
val pms = withContext(Dispatchers.IO) { db.getPaymentModes(projId) }
|
if (project != null) {
|
||||||
|
|
||||||
if (cats.isEmpty()) {
|
|
||||||
val defaults = CategoryUtils.getDefaultCategories(getApplication(), projId)
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
defaults.forEach { db.addCategory(it) }
|
db.ensureDefaultLabels(projId, project.type)
|
||||||
}
|
}
|
||||||
categories = withContext(Dispatchers.IO) { db.getCategories(projId) }
|
|
||||||
} else {
|
|
||||||
categories = cats
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pms.isEmpty()) {
|
|
||||||
val defaults = CategoryUtils.getDefaultPaymentModes(getApplication(), projId)
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
defaults.forEach { db.addPaymentMode(it) }
|
|
||||||
}
|
|
||||||
paymentModes = withContext(Dispatchers.IO) { db.getPaymentModes(projId) }
|
|
||||||
} else {
|
|
||||||
paymentModes = pms
|
|
||||||
}
|
}
|
||||||
|
categories = withContext(Dispatchers.IO) { db.getCategories(projId) }
|
||||||
|
paymentModes = withContext(Dispatchers.IO) { db.getPaymentModes(projId) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addCategory(name: String, icon: String, color: String) {
|
fun addCategory(name: String, icon: String, color: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
db.addCategory(DBCategory(0, 0, projectId, name, icon, color))
|
db.addCategoryAndSync(DBCategory(0, 0, projectId, name, icon, color, DBBill.STATE_ADDED))
|
||||||
}
|
}
|
||||||
loadLabels(projectId)
|
loadLabels(projectId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateCategory(id: Long, name: String, icon: String, color: String) {
|
fun updateCategory(cat: DBCategory, name: String, icon: String, color: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
db.updateCategory(id, name, icon, color)
|
db.updateCategoryAndSync(cat, name, icon, color)
|
||||||
}
|
}
|
||||||
loadLabels(projectId)
|
loadLabels(projectId)
|
||||||
}
|
}
|
||||||
@@ -74,7 +60,7 @@ class LabelManagementViewModel(application: Application) : AndroidViewModel(appl
|
|||||||
fun deleteCategory(id: Long) {
|
fun deleteCategory(id: Long) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
db.deleteCategory(id)
|
db.deleteCategoryAndSync(id)
|
||||||
}
|
}
|
||||||
loadLabels(projectId)
|
loadLabels(projectId)
|
||||||
}
|
}
|
||||||
@@ -83,16 +69,16 @@ class LabelManagementViewModel(application: Application) : AndroidViewModel(appl
|
|||||||
fun addPaymentMode(name: String, icon: String, color: String) {
|
fun addPaymentMode(name: String, icon: String, color: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
db.addPaymentMode(DBPaymentMode(0, 0, projectId, name, icon, color))
|
db.addPaymentModeAndSync(DBPaymentMode(0, 0, projectId, name, icon, color, DBBill.STATE_ADDED))
|
||||||
}
|
}
|
||||||
loadLabels(projectId)
|
loadLabels(projectId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updatePaymentMode(id: Long, name: String, icon: String, color: String) {
|
fun updatePaymentMode(pm: DBPaymentMode, name: String, icon: String, color: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
db.updatePaymentMode(id, name, icon, color)
|
db.updatePaymentModeAndSync(pm, name, icon, color)
|
||||||
}
|
}
|
||||||
loadLabels(projectId)
|
loadLabels(projectId)
|
||||||
}
|
}
|
||||||
@@ -101,7 +87,7 @@ class LabelManagementViewModel(application: Application) : AndroidViewModel(appl
|
|||||||
fun deletePaymentMode(id: Long) {
|
fun deletePaymentMode(id: Long) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
db.deletePaymentMode(id)
|
db.deletePaymentModeAndSync(id)
|
||||||
}
|
}
|
||||||
loadLabels(projectId)
|
loadLabels(projectId)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,10 @@ fun BillsListScreen(
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val sharedPreferences = remember { PreferenceManager.getDefaultSharedPreferences(context) }
|
val sharedPreferences = remember { PreferenceManager.getDefaultSharedPreferences(context) }
|
||||||
val showArchived = sharedPreferences.getBoolean(stringResource(R.string.pref_key_show_archived), false)
|
val showArchived = sharedPreferences.getBoolean(stringResource(R.string.pref_key_show_archived), false)
|
||||||
|
val keyBetaFeatures = stringResource(R.string.pref_key_beta_features)
|
||||||
|
var showBetaFeatures by remember(keyBetaFeatures) {
|
||||||
|
mutableStateOf(sharedPreferences.getBoolean(keyBetaFeatures, false))
|
||||||
|
}
|
||||||
|
|
||||||
StatefulAlertDialog(
|
StatefulAlertDialog(
|
||||||
state = viewModel.dialogState,
|
state = viewModel.dialogState,
|
||||||
@@ -158,7 +162,8 @@ fun BillsListScreen(
|
|||||||
isArchived = proj?.isArchived == true,
|
isArchived = proj?.isArchived == true,
|
||||||
projectType = proj?.type ?: ProjectType.LOCAL,
|
projectType = proj?.type ?: ProjectType.LOCAL,
|
||||||
accessLevel = proj?.myAccessLevel ?: DBProject.ACCESS_LEVEL_ADMIN,
|
accessLevel = proj?.myAccessLevel ?: DBProject.ACCESS_LEVEL_ADMIN,
|
||||||
isShareable = proj?.isShareable() ?: true
|
isShareable = proj?.isShareable() ?: true,
|
||||||
|
showBetaFeatures = showBetaFeatures
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,7 +332,7 @@ fun BillsListScreen(
|
|||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
if (!isSearchExpanded) {
|
if (!isSearchExpanded) {
|
||||||
if (viewModel.hasUnlabeledBills) {
|
if (showBetaFeatures && viewModel.hasUnlabeledBills) {
|
||||||
IconButton(onClick = onLabelBillsClick) {
|
IconButton(onClick = onLabelBillsClick) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.Category,
|
Icons.Default.Category,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.content.Intent
|
|||||||
import net.helcel.cowspent.R
|
import net.helcel.cowspent.R
|
||||||
import net.helcel.cowspent.model.*
|
import net.helcel.cowspent.model.*
|
||||||
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
|
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
|
||||||
|
import net.helcel.cowspent.util.CategoryUtils
|
||||||
import net.helcel.cowspent.util.IRefreshBillsListCallback
|
import net.helcel.cowspent.util.IRefreshBillsListCallback
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -98,6 +99,10 @@ object BillsListUtils {
|
|||||||
context: Context
|
context: Context
|
||||||
) {
|
) {
|
||||||
val timestamp = System.currentTimeMillis() / 1000
|
val timestamp = System.currentTimeMillis() / 1000
|
||||||
|
val proj = db.getProject(projectId)
|
||||||
|
val categories = db.getCategories(projectId)
|
||||||
|
val reimbursementCategoryId = CategoryUtils.getReimbursementCategoryId(categories, projectId, proj?.remoteId)
|
||||||
|
|
||||||
for (t in transactions) {
|
for (t in transactions) {
|
||||||
val owerId = t.owerMemberId
|
val owerId = t.owerMemberId
|
||||||
val receiverId = t.receiverMemberId
|
val receiverId = t.receiverMemberId
|
||||||
@@ -106,7 +111,7 @@ object BillsListUtils {
|
|||||||
0, 0, projectId, owerId, amount,
|
0, 0, projectId, owerId, amount,
|
||||||
timestamp, context.getString(R.string.settle_bill_what),
|
timestamp, context.getString(R.string.settle_bill_what),
|
||||||
DBBill.STATE_ADDED, DBBill.NON_REPEATED,
|
DBBill.STATE_ADDED, DBBill.NON_REPEATED,
|
||||||
DBBill.PAYMODE_NONE, DBBill.CATEGORY_NONE,
|
DBBill.PAYMODE_NONE, reimbursementCategoryId,
|
||||||
"", DBBill.PAYMODE_ID_NONE
|
"", DBBill.PAYMODE_ID_NONE
|
||||||
)
|
)
|
||||||
bill.billOwers += DBBillOwer(0, 0, receiverId)
|
bill.billOwers += DBBillOwer(0, 0, receiverId)
|
||||||
|
|||||||
@@ -21,9 +21,7 @@ import androidx.activity.viewModels
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.AddCircleOutline
|
|
||||||
import androidx.compose.material.icons.filled.Sync
|
import androidx.compose.material.icons.filled.Sync
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
@@ -50,6 +48,7 @@ import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
|
|||||||
import net.helcel.cowspent.persistence.CowspentServerSyncHelper
|
import net.helcel.cowspent.persistence.CowspentServerSyncHelper
|
||||||
import net.helcel.cowspent.theme.ThemeUtils
|
import net.helcel.cowspent.theme.ThemeUtils
|
||||||
import net.helcel.cowspent.util.BillFormatter
|
import net.helcel.cowspent.util.BillFormatter
|
||||||
|
import net.helcel.cowspent.util.CategoryUtils
|
||||||
import net.helcel.cowspent.util.CospendClientUtil
|
import net.helcel.cowspent.util.CospendClientUtil
|
||||||
import net.helcel.cowspent.util.ExportUtil
|
import net.helcel.cowspent.util.ExportUtil
|
||||||
import net.helcel.cowspent.util.ICallback
|
import net.helcel.cowspent.util.ICallback
|
||||||
@@ -115,18 +114,8 @@ class BillsListViewActivity :
|
|||||||
Log.d(TAG, "CREATED project id: $pid")
|
Log.d(TAG, "CREATED project id: $pid")
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val addedProj = withContext(Dispatchers.IO) { db.getProject(pid) }
|
val addedProj = withContext(Dispatchers.IO) { db.getProject(pid) }
|
||||||
val message: String
|
val message = getString(R.string.msg_project_added, addedProj?.name?.ifEmpty { addedProj.remoteId } ?: pid.toString())
|
||||||
val title: String
|
showToast(this@BillsListViewActivity, message)
|
||||||
if (created) {
|
|
||||||
Log.e(TAG, "CREATED !!!")
|
|
||||||
title = getString(R.string.msg_project_added)
|
|
||||||
message = getString(R.string.msg_project_added, addedProj?.remoteId)
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "ADDED !!!")
|
|
||||||
title = getString(R.string.msg_project_added)
|
|
||||||
message = getString(R.string.msg_project_added, addedProj?.remoteId)
|
|
||||||
}
|
|
||||||
showDialog(message, title, Icons.Default.AddCircleOutline)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +131,7 @@ class BillsListViewActivity :
|
|||||||
}
|
}
|
||||||
if (!db.cowspentServerSyncHelper.isSyncPossible) {
|
if (!db.cowspentServerSyncHelper.isSyncPossible) {
|
||||||
if (CowspentServerSyncHelper.isNextcloudAccountConfigured(applicationContext)) {
|
if (CowspentServerSyncHelper.isNextcloudAccountConfigured(applicationContext)) {
|
||||||
Toast.makeText(applicationContext, getString(R.string.error_sync, getString(CospendClientUtil.LoginStatus.NO_NETWORK.str)), Toast.LENGTH_LONG).show()
|
showToast(this@BillsListViewActivity, getString(R.string.error_sync, getString(CospendClientUtil.LoginStatus.NO_NETWORK.str)), Toast.LENGTH_LONG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -565,11 +554,13 @@ class BillsListViewActivity :
|
|||||||
|
|
||||||
val members = db.getMembersOfProject(proj.id, null)
|
val members = db.getMembersOfProject(proj.id, null)
|
||||||
val bills = db.getBillsOfProject(proj.id)
|
val bills = db.getBillsOfProject(proj.id)
|
||||||
|
val categories = db.getCategories(proj.id)
|
||||||
|
val reimbursementCategoryId = CategoryUtils.getReimbursementCategoryId(categories, proj.id, proj.remoteId)
|
||||||
val balances = HashMap<Long, Double>()
|
val balances = HashMap<Long, Double>()
|
||||||
SupportUtil.getStats(
|
SupportUtil.getStats(
|
||||||
members, bills,
|
members, bills,
|
||||||
mutableMapOf(), balances, mutableMapOf(), mutableMapOf(),
|
mutableMapOf(), balances, mutableMapOf(), mutableMapOf(),
|
||||||
-1000, -1000, null, null
|
-1000L, -1000L, reimbursementCategoryId, null, null
|
||||||
)
|
)
|
||||||
Triple(proj, members, balances)
|
Triple(proj, members, balances)
|
||||||
}
|
}
|
||||||
@@ -636,13 +627,13 @@ class BillsListViewActivity :
|
|||||||
mid == null || mid == it.payerId || it.billOwersIds.contains(mid)
|
mid == null || mid == it.payerId || it.billOwersIds.contains(mid)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.hasUnlabeledBills = bills.any { it.categoryRemoteId == 0 && it.state != DBBill.STATE_DELETED }
|
viewModel.hasUnlabeledBills = bills.any { it.categoryId == 0L && it.state != DBBill.STATE_DELETED }
|
||||||
|
|
||||||
val projectMembers = db.getMembersOfProject(projId, null)
|
val projectMembers = db.getMembersOfProject(projId, null)
|
||||||
val memberMap = projectMembers.associateBy { it.id }
|
val memberMap = projectMembers.associateBy { it.id }
|
||||||
|
|
||||||
val projectPaymentModes = db.getPaymentModes(projId).associateBy { it.remoteId }
|
val projectPaymentModes = db.getPaymentModes(projId).associateBy { it.id }
|
||||||
val projectCategories = db.getCategories(projId).associateBy { it.remoteId }
|
val projectCategories = db.getCategories(projId).associateBy { it.id }
|
||||||
|
|
||||||
BillFormatter.formatBills(
|
BillFormatter.formatBills(
|
||||||
bills,
|
bills,
|
||||||
@@ -708,15 +699,6 @@ class BillsListViewActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showDialog(msg: String, title: String, icon: ImageVector) {
|
|
||||||
viewModel.showDialog(
|
|
||||||
title = title,
|
|
||||||
message = msg,
|
|
||||||
positiveText = getString(android.R.string.ok),
|
|
||||||
icon = icon
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateUsernameInDrawer() {
|
private fun updateUsernameInDrawer() {
|
||||||
if (!CowspentServerSyncHelper.isNextcloudAccountConfigured(this)) {
|
if (!CowspentServerSyncHelper.isNextcloudAccountConfigured(this)) {
|
||||||
val text = getString(R.string.drawer_no_account)
|
val text = getString(R.string.drawer_no_account)
|
||||||
|
|||||||
@@ -118,8 +118,8 @@ object ProjectImportHelper {
|
|||||||
val payerWeight = if (columns.containsKey("payer_weight")) line[columns["payer_weight"]!!].toDouble() else 1.0
|
val payerWeight = if (columns.containsKey("payer_weight")) line[columns["payer_weight"]!!].toDouble() else 1.0
|
||||||
val owersStr = if (columns.containsKey("owers")) line[columns["owers"]!!] else ""
|
val owersStr = if (columns.containsKey("owers")) line[columns["owers"]!!] else ""
|
||||||
val payerActive = columns.containsKey("payer_active") && line[columns["payer_active"]!!] == "1"
|
val payerActive = columns.containsKey("payer_active") && line[columns["payer_active"]!!] == "1"
|
||||||
val catId = if (columns.containsKey("categoryid") && line[columns["categoryid"]!!].isNotEmpty()) line[columns["categoryid"]!!].toInt() else 0
|
val catId = if (columns.containsKey("categoryid") && line[columns["categoryid"]!!].isNotEmpty()) line[columns["categoryid"]!!].toLong() else 0L
|
||||||
val pmId = if (columns.containsKey("paymentmodeid") && line[columns["paymentmodeid"]!!].isNotEmpty()) line[columns["paymentmodeid"]!!].toInt() else 0
|
val pmId = if (columns.containsKey("paymentmodeid") && line[columns["paymentmodeid"]!!].isNotEmpty()) line[columns["paymentmodeid"]!!].toLong() else 0L
|
||||||
val pm = if (columns.containsKey("paymentmode")) line[columns["paymentmode"]!!] else null
|
val pm = if (columns.containsKey("paymentmode")) line[columns["paymentmode"]!!] else null
|
||||||
|
|
||||||
membersActive[payerName] = payerActive
|
membersActive[payerName] = payerActive
|
||||||
@@ -149,9 +149,17 @@ object ProjectImportHelper {
|
|||||||
|
|
||||||
val memberNameToId = mutableMapOf<String, Long>()
|
val memberNameToId = mutableMapOf<String, Long>()
|
||||||
val pid = db.addProject(DBProject(0, projectRemoteId, "", projectRemoteId, null, null, null, ProjectType.LOCAL, 0L, mainCurrencyName, false, DBProject.ACCESS_LEVEL_UNKNOWN, null))
|
val pid = db.addProject(DBProject(0, projectRemoteId, "", projectRemoteId, null, null, null, ProjectType.LOCAL, 0L, mainCurrencyName, false, DBProject.ACCESS_LEVEL_UNKNOWN, null))
|
||||||
|
|
||||||
paymentModes.forEach { db.addPaymentMode(DBPaymentMode(0, it.remoteId, pid, it.name, it.icon, it.color)) }
|
val pmRemoteToLocal = mutableMapOf<Long, Long>()
|
||||||
categories.forEach { db.addCategory(DBCategory(0, it.remoteId, pid, it.name, it.icon, it.color)) }
|
paymentModes.forEach {
|
||||||
|
val newId = db.addPaymentMode(DBPaymentMode(0, it.remoteId, pid, it.name, it.icon, it.color))
|
||||||
|
pmRemoteToLocal[it.remoteId] = newId
|
||||||
|
}
|
||||||
|
val catRemoteToLocal = mutableMapOf<Long, Long>()
|
||||||
|
categories.forEach {
|
||||||
|
val newId = db.addCategory(DBCategory(0, it.remoteId, pid, it.name, it.icon, it.color))
|
||||||
|
catRemoteToLocal[it.remoteId] = newId
|
||||||
|
}
|
||||||
currencies.forEach { db.addCurrency(DBCurrency(0, 0, pid, it.name, it.exchangeRate, DBBill.STATE_OK)) }
|
currencies.forEach { db.addCurrency(DBCurrency(0, 0, pid, it.name, it.exchangeRate, DBBill.STATE_OK)) }
|
||||||
|
|
||||||
membersWeight.keys.forEach { mName ->
|
membersWeight.keys.forEach { mName ->
|
||||||
@@ -160,7 +168,9 @@ object ProjectImportHelper {
|
|||||||
|
|
||||||
bills.forEach { b ->
|
bills.forEach { b ->
|
||||||
val payerId = memberNameToId[billRemoteIdToPayerName[b.remoteId]] ?: 0L
|
val payerId = memberNameToId[billRemoteIdToPayerName[b.remoteId]] ?: 0L
|
||||||
val billId = db.addBill(DBBill(0, 0, pid, payerId, b.amount, b.timestamp, b.what, DBBill.STATE_OK, b.repeat, b.paymentMode, b.categoryRemoteId, b.comment, b.paymentModeRemoteId))
|
val localCatId = catRemoteToLocal[b.categoryId] ?: 0L
|
||||||
|
val localPmId = pmRemoteToLocal[b.paymentModeId] ?: 0L
|
||||||
|
val billId = db.addBill(DBBill(0, 0, pid, payerId, b.amount, b.timestamp, b.what, DBBill.STATE_OK, b.repeat, b.paymentMode, localCatId, b.comment, localPmId))
|
||||||
billRemoteIdToOwerStr[b.remoteId]?.split(", ")?.filter { it.isNotEmpty() }?.forEach { ower ->
|
billRemoteIdToOwerStr[b.remoteId]?.split(", ")?.filter { it.isNotEmpty() }?.forEach { ower ->
|
||||||
memberNameToId[ower.trim()]?.let { owerId -> db.addBillower(billId, owerId) }
|
memberNameToId[ower.trim()]?.let { owerId -> db.addBillower(billId, owerId) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ fun ProjectOptionsDialogContent(
|
|||||||
isArchived: Boolean = false,
|
isArchived: Boolean = false,
|
||||||
projectType: ProjectType = ProjectType.LOCAL,
|
projectType: ProjectType = ProjectType.LOCAL,
|
||||||
accessLevel: Int = DBProject.ACCESS_LEVEL_ADMIN,
|
accessLevel: Int = DBProject.ACCESS_LEVEL_ADMIN,
|
||||||
isShareable: Boolean = true
|
isShareable: Boolean = true,
|
||||||
|
showBetaFeatures: Boolean = false
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
shape = MaterialTheme.shapes.large,
|
shape = MaterialTheme.shapes.large,
|
||||||
@@ -79,7 +80,7 @@ fun ProjectOptionsDialogContent(
|
|||||||
// Row 2: Manage Member, Manage Labels, Manage Currencies
|
// Row 2: Manage Member, Manage Labels, Manage Currencies
|
||||||
if (!isArchived && isMaintainer) {
|
if (!isArchived && isMaintainer) {
|
||||||
row2.add(ProjectOption(stringResource(R.string.action_members), Icons.Default.Group, onManageMembers))
|
row2.add(ProjectOption(stringResource(R.string.action_members), Icons.Default.Group, onManageMembers))
|
||||||
if (projectType == ProjectType.LOCAL || projectType == ProjectType.COSPEND) {
|
if (showBetaFeatures && (projectType == ProjectType.LOCAL || projectType == ProjectType.COSPEND)) {
|
||||||
row2.add(ProjectOption(stringResource(R.string.action_labels), Icons.AutoMirrored.Filled.Label, onManageLabels))
|
row2.add(ProjectOption(stringResource(R.string.action_labels), Icons.AutoMirrored.Filled.Label, onManageLabels))
|
||||||
}
|
}
|
||||||
row2.add(ProjectOption(stringResource(R.string.action_currencies), Icons.Default.MonetizationOn, onManageCurrencies))
|
row2.add(ProjectOption(stringResource(R.string.action_currencies), Icons.Default.MonetizationOn, onManageCurrencies))
|
||||||
@@ -181,7 +182,8 @@ fun ProjectOptionsDialogPreview() {
|
|||||||
isArchived = false,
|
isArchived = false,
|
||||||
projectType = ProjectType.COSPEND,
|
projectType = ProjectType.COSPEND,
|
||||||
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
|
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
|
||||||
isShareable = true
|
isShareable = true,
|
||||||
|
showBetaFeatures = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,7 +207,8 @@ fun ProjectOptionsDialogPreview2() {
|
|||||||
isArchived = true,
|
isArchived = true,
|
||||||
projectType = ProjectType.COSPEND,
|
projectType = ProjectType.COSPEND,
|
||||||
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
|
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
|
||||||
isShareable = true
|
isShareable = true,
|
||||||
|
showBetaFeatures = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -228,7 +231,8 @@ fun ProjectOptionsDialogPreview3() {
|
|||||||
isArchived = false,
|
isArchived = false,
|
||||||
projectType = ProjectType.LOCAL,
|
projectType = ProjectType.LOCAL,
|
||||||
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
|
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
|
||||||
isShareable = true
|
isShareable = true,
|
||||||
|
showBetaFeatures = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,7 +320,6 @@ class NewProjectActivity : AppCompatActivity() {
|
|||||||
pid
|
pid
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
showToast(getString(R.string.project_added_success), Toast.LENGTH_LONG)
|
|
||||||
return pid
|
return pid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ fun SettingsScreen(
|
|||||||
val keyColor = stringResource(R.string.pref_key_color)
|
val keyColor = stringResource(R.string.pref_key_color)
|
||||||
val keyOfflineMode = stringResource(R.string.pref_key_offline_mode)
|
val keyOfflineMode = stringResource(R.string.pref_key_offline_mode)
|
||||||
val keyShowArchived = stringResource(R.string.pref_key_show_archived)
|
val keyShowArchived = stringResource(R.string.pref_key_show_archived)
|
||||||
|
val keyBetaFeatures = stringResource(R.string.pref_key_beta_features)
|
||||||
|
|
||||||
// States for preferences
|
// States for preferences
|
||||||
var nightMode by remember(keyNightMode) {
|
var nightMode by remember(keyNightMode) {
|
||||||
@@ -113,6 +114,9 @@ fun SettingsScreen(
|
|||||||
var showArchived by remember(keyShowArchived) {
|
var showArchived by remember(keyShowArchived) {
|
||||||
mutableStateOf(sharedPreferences.getBoolean(keyShowArchived, false))
|
mutableStateOf(sharedPreferences.getBoolean(keyShowArchived, false))
|
||||||
}
|
}
|
||||||
|
var betaFeatures by remember(keyBetaFeatures) {
|
||||||
|
mutableStateOf(sharedPreferences.getBoolean(keyBetaFeatures, false))
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -242,6 +246,19 @@ fun SettingsScreen(
|
|||||||
// Other
|
// Other
|
||||||
SettingsCategory(stringResource(R.string.settings_other))
|
SettingsCategory(stringResource(R.string.settings_other))
|
||||||
|
|
||||||
|
SettingsSwitchPreference(
|
||||||
|
title = stringResource(R.string.settings_beta_features),
|
||||||
|
summary = stringResource(R.string.settings_beta_features_summary),
|
||||||
|
icon = Icons.Default.Info,
|
||||||
|
checked = betaFeatures,
|
||||||
|
onCheckedChange = {
|
||||||
|
betaFeatures = it
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putBoolean(keyBetaFeatures, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
SettingsPreference(
|
SettingsPreference(
|
||||||
title = stringResource(R.string.title_about),
|
title = stringResource(R.string.title_about),
|
||||||
icon = Icons.Default.Info,
|
icon = Icons.Default.Info,
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ import net.helcel.cowspent.android.helper.formatShortValue
|
|||||||
import net.helcel.cowspent.model.DBBill
|
import net.helcel.cowspent.model.DBBill
|
||||||
import net.helcel.cowspent.model.DBCategory
|
import net.helcel.cowspent.model.DBCategory
|
||||||
import net.helcel.cowspent.model.DBMember
|
import net.helcel.cowspent.model.DBMember
|
||||||
|
import net.helcel.cowspent.util.CategoryUtils
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@@ -87,6 +88,8 @@ private object SankeyDimens {
|
|||||||
@Composable
|
@Composable
|
||||||
fun ProjectSankeyDiagram(
|
fun ProjectSankeyDiagram(
|
||||||
projectName: String,
|
projectName: String,
|
||||||
|
projectId: Long,
|
||||||
|
projectRemoteId: String?,
|
||||||
allMembers: List<DBMember>,
|
allMembers: List<DBMember>,
|
||||||
allBills: List<DBBill>,
|
allBills: List<DBBill>,
|
||||||
customCategories: List<DBCategory>,
|
customCategories: List<DBCategory>,
|
||||||
@@ -96,12 +99,15 @@ fun ProjectSankeyDiagram(
|
|||||||
var selectedMemberId by remember { mutableLongStateOf(-1L) }
|
var selectedMemberId by remember { mutableLongStateOf(-1L) }
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val activeBills = remember(allBills) {
|
val reimbursementCategoryId = remember(customCategories, projectId, projectRemoteId) {
|
||||||
allBills.filter { it.state != DBBill.STATE_DELETED && it.categoryRemoteId != DBBill.CATEGORY_REIMBURSEMENT }
|
CategoryUtils.getReimbursementCategoryId(customCategories, projectId, projectRemoteId)
|
||||||
|
}
|
||||||
|
val activeBills = remember(allBills, reimbursementCategoryId) {
|
||||||
|
allBills.filter { it.state != DBBill.STATE_DELETED && it.categoryId != reimbursementCategoryId }
|
||||||
}
|
}
|
||||||
|
|
||||||
val membersMap = remember(allMembers) { allMembers.associateBy { it.id } }
|
val membersMap = remember(allMembers) { allMembers.associateBy { it.id } }
|
||||||
val categoriesMap = remember(customCategories) { customCategories.associateBy { it.remoteId.toInt() } }
|
val categoriesMap = remember(customCategories) { customCategories.associateBy { it.id } }
|
||||||
|
|
||||||
if (activeBills.isEmpty()) {
|
if (activeBills.isEmpty()) {
|
||||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
@@ -112,13 +118,13 @@ fun ProjectSankeyDiagram(
|
|||||||
|
|
||||||
val spendings = remember(activeBills, selectedMemberId, membersMap) {
|
val spendings = remember(activeBills, selectedMemberId, membersMap) {
|
||||||
val spentMap = mutableMapOf<Long, Double>().apply { membersMap.keys.forEach { put(it, 0.0) } }
|
val spentMap = mutableMapOf<Long, Double>().apply { membersMap.keys.forEach { put(it, 0.0) } }
|
||||||
val catMap = mutableMapOf<Int, Double>()
|
val catMap = mutableMapOf<Long, Double>()
|
||||||
|
|
||||||
activeBills.forEach { bill ->
|
activeBills.forEach { bill ->
|
||||||
val totalWeight = bill.billOwers.sumOf { membersMap[it.memberId]?.weight ?: 1.0 }
|
val totalWeight = bill.billOwers.sumOf { membersMap[it.memberId]?.weight ?: 1.0 }
|
||||||
if (totalWeight > 0) {
|
if (totalWeight > 0) {
|
||||||
if (selectedMemberId == -1L) {
|
if (selectedMemberId == -1L) {
|
||||||
catMap[bill.categoryRemoteId] = (catMap[bill.categoryRemoteId] ?: 0.0) + bill.amount
|
catMap[bill.categoryId] = (catMap[bill.categoryId] ?: 0.0) + bill.amount
|
||||||
bill.billOwers.forEach { bo ->
|
bill.billOwers.forEach { bo ->
|
||||||
val weight = membersMap[bo.memberId]?.weight ?: 1.0
|
val weight = membersMap[bo.memberId]?.weight ?: 1.0
|
||||||
spentMap[bo.memberId] = (spentMap[bo.memberId] ?: 0.0) + (bill.amount / totalWeight) * weight
|
spentMap[bo.memberId] = (spentMap[bo.memberId] ?: 0.0) + (bill.amount / totalWeight) * weight
|
||||||
@@ -126,7 +132,7 @@ fun ProjectSankeyDiagram(
|
|||||||
} else {
|
} else {
|
||||||
bill.billOwers.find { it.memberId == selectedMemberId }?.let { bo ->
|
bill.billOwers.find { it.memberId == selectedMemberId }?.let { bo ->
|
||||||
val weight = membersMap[bo.memberId]?.weight ?: 1.0
|
val weight = membersMap[bo.memberId]?.weight ?: 1.0
|
||||||
catMap[bill.categoryRemoteId] = (catMap[bill.categoryRemoteId] ?: 0.0) + (bill.amount / totalWeight) * weight
|
catMap[bill.categoryId] = (catMap[bill.categoryId] ?: 0.0) + (bill.amount / totalWeight) * weight
|
||||||
spentMap[selectedMemberId] = (spentMap[selectedMemberId] ?: 0.0) + (bill.amount / totalWeight) * weight
|
spentMap[selectedMemberId] = (spentMap[selectedMemberId] ?: 0.0) + (bill.amount / totalWeight) * weight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,9 +221,9 @@ private fun SankeyContent(
|
|||||||
selectedMemberId: Long,
|
selectedMemberId: Long,
|
||||||
totalAmount: Double,
|
totalAmount: Double,
|
||||||
displayMemberSpendings: List<Pair<Long, Double>>,
|
displayMemberSpendings: List<Pair<Long, Double>>,
|
||||||
displayCategorySpendings: List<Pair<Int, Double>>,
|
displayCategorySpendings: List<Pair<Long, Double>>,
|
||||||
membersMap: Map<Long, DBMember>,
|
membersMap: Map<Long, DBMember>,
|
||||||
categoriesMap: Map<Int, DBCategory>
|
categoriesMap: Map<Long, DBCategory>
|
||||||
) {
|
) {
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
@@ -399,4 +405,4 @@ private fun DrawScope.drawSankeyFlow(startX: Float, startY: Float, startWidth: F
|
|||||||
|
|
||||||
@Preview(showBackground = true, widthDp = 360, heightDp = 640)
|
@Preview(showBackground = true, widthDp = 360, heightDp = 640)
|
||||||
@Composable
|
@Composable
|
||||||
fun ProjectSankeyDiagramPreview() = MaterialTheme { ProjectSankeyDiagram("Test Project", StatisticsMockData.members, StatisticsMockData.bills, emptyList()) {} }
|
fun ProjectSankeyDiagramPreview() = MaterialTheme { ProjectSankeyDiagram("Test Project", 1L, "1", StatisticsMockData.members, StatisticsMockData.bills, emptyList()) {} }
|
||||||
|
|||||||
+21
-11
@@ -84,6 +84,21 @@ fun ProjectStatisticsScreen(
|
|||||||
|
|
||||||
val statsData by produceState<StatisticsData?>(null, proj.id) {
|
val statsData by produceState<StatisticsData?>(null, proj.id) {
|
||||||
value = withContext(Dispatchers.IO) {
|
value = withContext(Dispatchers.IO) {
|
||||||
|
val syncedCategories = db.getCategories(proj.id)
|
||||||
|
val defaultCategories = CategoryUtils.getDefaultCategories(context, proj.id)
|
||||||
|
|
||||||
|
val toEnsure = if (proj.type == ProjectType.LOCAL) {
|
||||||
|
defaultCategories
|
||||||
|
} else {
|
||||||
|
listOfNotNull(defaultCategories.find { it.remoteId == DBBill.CATEGORY_REIMBURSEMENT })
|
||||||
|
}
|
||||||
|
|
||||||
|
toEnsure.forEach { def ->
|
||||||
|
if (syncedCategories.none { it.remoteId == def.remoteId }) {
|
||||||
|
db.addCategory(def)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val members = db.getMembersOfProject(proj.id, null)
|
val members = db.getMembersOfProject(proj.id, null)
|
||||||
val bills = db.getBillsOfProject(proj.id)
|
val bills = db.getBillsOfProject(proj.id)
|
||||||
val categories = db.getCategories(proj.id)
|
val categories = db.getCategories(proj.id)
|
||||||
@@ -94,19 +109,12 @@ fun ProjectStatisticsScreen(
|
|||||||
|
|
||||||
if (statsData != null) {
|
if (statsData != null) {
|
||||||
val data = statsData!!
|
val data = statsData!!
|
||||||
val defaultCategories = remember(proj.id) { CategoryUtils.getDefaultCategories(context, proj.id) }
|
val categories = data.categories
|
||||||
val categories = remember(proj.type, data.categories, defaultCategories) {
|
|
||||||
val hardcoded = if (proj.type == ProjectType.LOCAL) {
|
|
||||||
defaultCategories
|
|
||||||
} else {
|
|
||||||
listOfNotNull(defaultCategories.find { it.remoteId.toInt() == DBBill.CATEGORY_REIMBURSEMENT })
|
|
||||||
}
|
|
||||||
(data.categories + hardcoded).distinctBy { it.remoteId }
|
|
||||||
}
|
|
||||||
val categoryNoneLabel = stringResource(R.string.category_none)
|
val categoryNoneLabel = stringResource(R.string.category_none)
|
||||||
val sankeyCategories = remember(proj.id, data.categories, defaultCategories, categoryNoneLabel) {
|
val sankeyCategories = remember(proj.id, data.categories, categoryNoneLabel) {
|
||||||
val noneCategory = DBCategory(0, 0, proj.id, categoryNoneLabel, "❌", "#9E9E9E")
|
val noneCategory = DBCategory(0, 0, proj.id, categoryNoneLabel, "❌", "#9E9E9E")
|
||||||
(data.categories + defaultCategories + noneCategory).distinctBy { it.remoteId }
|
(data.categories + noneCategory).distinctBy { if (it.id == 0L) "none" else it.id.toString() }
|
||||||
}
|
}
|
||||||
|
|
||||||
when (selectedTab) {
|
when (selectedTab) {
|
||||||
@@ -131,6 +139,8 @@ fun ProjectStatisticsScreen(
|
|||||||
2 -> {
|
2 -> {
|
||||||
ProjectSankeyDiagram(
|
ProjectSankeyDiagram(
|
||||||
projectName = proj.name.ifEmpty { proj.remoteId },
|
projectName = proj.name.ifEmpty { proj.remoteId },
|
||||||
|
projectId = proj.id,
|
||||||
|
projectRemoteId = proj.remoteId,
|
||||||
allMembers = data.members,
|
allMembers = data.members,
|
||||||
allBills = data.bills,
|
allBills = data.bills,
|
||||||
customCategories = sankeyCategories,
|
customCategories = sankeyCategories,
|
||||||
|
|||||||
+14
-12
@@ -44,8 +44,8 @@ fun ProjectStatisticsTable(
|
|||||||
val sdf = remember { SimpleDateFormat("yyyy-MM-dd", Locale.ROOT) }
|
val sdf = remember { SimpleDateFormat("yyyy-MM-dd", Locale.ROOT) }
|
||||||
val dateFormat = remember { android.text.format.DateFormat.getDateFormat(context) }
|
val dateFormat = remember { android.text.format.DateFormat.getDateFormat(context) }
|
||||||
|
|
||||||
var categoryId by remember { mutableIntStateOf(-1000) }
|
var categoryId by remember { mutableLongStateOf(-1000L) }
|
||||||
var paymentModeId by remember { mutableIntStateOf(-1000) }
|
var paymentModeId by remember { mutableLongStateOf(-1000L) }
|
||||||
var dateMin by remember { mutableStateOf<String?>(null) }
|
var dateMin by remember { mutableStateOf<String?>(null) }
|
||||||
var dateMax by remember { mutableStateOf<String?>(null) }
|
var dateMax by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
@@ -61,10 +61,10 @@ fun ProjectStatisticsTable(
|
|||||||
val shareStatsIntro = stringResource(R.string.msg_stats_intro, proj.name.ifEmpty { proj.remoteId })
|
val shareStatsIntro = stringResource(R.string.msg_stats_intro, proj.name.ifEmpty { proj.remoteId })
|
||||||
|
|
||||||
val categories = remember(proj.id, customCategories, categoryAll, categoryNone, categoryReimbursement, categoryAllExceptReimbursement) {
|
val categories = remember(proj.id, customCategories, categoryAll, categoryNone, categoryReimbursement, categoryAllExceptReimbursement) {
|
||||||
val list = mutableListOf<Triple<Int, String, String>>()
|
val list = mutableListOf<Triple<Long, String, String>>()
|
||||||
list.add(Triple(-1000, "📋", categoryAll))
|
list.add(Triple(-1000L, "📋", categoryAll))
|
||||||
list.add(Triple(-100, "🧾", categoryAllExceptReimbursement))
|
list.add(Triple(-100L, "🧾", categoryAllExceptReimbursement))
|
||||||
list.add(Triple(0, "❌", categoryNone))
|
list.add(Triple(0L, "❌", categoryNone))
|
||||||
|
|
||||||
val catsToUse = if (proj.type == ProjectType.LOCAL) {
|
val catsToUse = if (proj.type == ProjectType.LOCAL) {
|
||||||
CategoryUtils.getDefaultCategories(context, proj.id)
|
CategoryUtils.getDefaultCategories(context, proj.id)
|
||||||
@@ -75,15 +75,15 @@ fun ProjectStatisticsTable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
catsToUse.forEach {
|
catsToUse.forEach {
|
||||||
list.add(Triple(it.remoteId.toInt(), it.icon, it.name ?: ""))
|
list.add(Triple(it.id, it.icon, it.name ?: ""))
|
||||||
}
|
}
|
||||||
list.distinctBy { it.first }
|
list.distinctBy { it.first }
|
||||||
}
|
}
|
||||||
|
|
||||||
val paymentModes = remember(proj.id, customPaymentModes, paymentModeAll, paymentModeNone) {
|
val paymentModes = remember(proj.id, customPaymentModes, paymentModeAll, paymentModeNone) {
|
||||||
val list = mutableListOf<Triple<Int, String, String>>()
|
val list = mutableListOf<Triple<Long, String, String>>()
|
||||||
list.add(Triple(-1000, "💳", paymentModeAll))
|
list.add(Triple(-1000L, "💳", paymentModeAll))
|
||||||
list.add(Triple(0, "❌", paymentModeNone))
|
list.add(Triple(0L, "❌", paymentModeNone))
|
||||||
|
|
||||||
val pmsToUse = if (proj.type == ProjectType.LOCAL) {
|
val pmsToUse = if (proj.type == ProjectType.LOCAL) {
|
||||||
CategoryUtils.getDefaultPaymentModes(context, proj.id)
|
CategoryUtils.getDefaultPaymentModes(context, proj.id)
|
||||||
@@ -94,7 +94,7 @@ fun ProjectStatisticsTable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pmsToUse.forEach {
|
pmsToUse.forEach {
|
||||||
list.add(Triple(it.remoteId.toInt(), it.icon, it.name ?: ""))
|
list.add(Triple(it.id, it.icon, it.name ?: ""))
|
||||||
}
|
}
|
||||||
list.distinctBy { it.first }
|
list.distinctBy { it.first }
|
||||||
}
|
}
|
||||||
@@ -105,10 +105,12 @@ fun ProjectStatisticsTable(
|
|||||||
val membersPaid = HashMap<Long, Double>()
|
val membersPaid = HashMap<Long, Double>()
|
||||||
val membersSpent = HashMap<Long, Double>()
|
val membersSpent = HashMap<Long, Double>()
|
||||||
|
|
||||||
|
val reimbursementCategoryId = CategoryUtils.getReimbursementCategoryId(customCategories, proj.id, proj.remoteId)
|
||||||
|
|
||||||
SupportUtil.getStats(
|
SupportUtil.getStats(
|
||||||
allMembers, allBills,
|
allMembers, allBills,
|
||||||
membersNbBills, membersBalance, membersPaid, membersSpent,
|
membersNbBills, membersBalance, membersPaid, membersSpent,
|
||||||
categoryId, paymentModeId, dateMin, dateMax
|
categoryId, paymentModeId, reimbursementCategoryId, dateMin, dateMax
|
||||||
)
|
)
|
||||||
|
|
||||||
var statsText = shareStatsIntro + "\n\n"
|
var statsText = shareStatsIntro + "\n\n"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package net.helcel.cowspent.model
|
package net.helcel.cowspent.model
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
@@ -16,9 +15,9 @@ open class DBBill(
|
|||||||
var state: Int,
|
var state: Int,
|
||||||
var repeat: String?,
|
var repeat: String?,
|
||||||
var paymentMode: String?,
|
var paymentMode: String?,
|
||||||
var categoryRemoteId: Int,
|
var categoryId: Long,
|
||||||
var comment: String?,
|
var comment: String?,
|
||||||
var paymentModeRemoteId: Int
|
var paymentModeId: Long
|
||||||
) : Item, Serializable {
|
) : Item, Serializable {
|
||||||
|
|
||||||
var formattedWhat: String = ""
|
var formattedWhat: String = ""
|
||||||
@@ -39,7 +38,6 @@ open class DBBill(
|
|||||||
get() {
|
get() {
|
||||||
val cal = Calendar.getInstance()
|
val cal = Calendar.getInstance()
|
||||||
cal.timeInMillis = timestamp * 1000
|
cal.timeInMillis = timestamp * 1000
|
||||||
Log.v("ll", "[$what] get date ts $timestamp year ${cal[Calendar.YEAR]}")
|
|
||||||
val month = cal[Calendar.MONTH] + 1
|
val month = cal[Calendar.MONTH] + 1
|
||||||
val day = cal[Calendar.DAY_OF_MONTH]
|
val day = cal[Calendar.DAY_OF_MONTH]
|
||||||
return "${cal[Calendar.YEAR]}-${String.format(Locale.ROOT, "%02d", month)}-${
|
return "${cal[Calendar.YEAR]}-${String.format(Locale.ROOT, "%02d", month)}-${
|
||||||
@@ -58,7 +56,7 @@ open class DBBill(
|
|||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "#DBBill$id/$remoteId,$projectId, $payerId, $amount, $timestamp, $what, $state, $repeat, $paymentMode, $categoryRemoteId"
|
return "#DBBill$id/$remoteId,$projectId, $payerId, $amount, $timestamp, $what, $state, $repeat, $paymentMode, $categoryId"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isSection(): Boolean {
|
override fun isSection(): Boolean {
|
||||||
@@ -73,15 +71,15 @@ open class DBBill(
|
|||||||
const val PAYMODE_TRANSFER = "t"
|
const val PAYMODE_TRANSFER = "t"
|
||||||
const val PAYMODE_ONLINE_SERVICE = "o"
|
const val PAYMODE_ONLINE_SERVICE = "o"
|
||||||
|
|
||||||
const val PAYMODE_ID_NONE = 0
|
const val PAYMODE_ID_NONE = 0L
|
||||||
const val PAYMODE_ID_CARD = -1
|
const val PAYMODE_ID_CARD = -1L
|
||||||
const val PAYMODE_ID_CASH = -2
|
const val PAYMODE_ID_CASH = -2L
|
||||||
const val PAYMODE_ID_CHECK = -3
|
const val PAYMODE_ID_CHECK = -3L
|
||||||
const val PAYMODE_ID_TRANSFER = -4
|
const val PAYMODE_ID_TRANSFER = -4L
|
||||||
const val PAYMODE_ID_ONLINE_SERVICE = -5
|
const val PAYMODE_ID_ONLINE_SERVICE = -5L
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val oldPmIdToNew: Map<String, Int> = object : HashMap<String, Int>() {
|
val oldPmIdToNew: Map<String, Long> = object : HashMap<String, Long>() {
|
||||||
init {
|
init {
|
||||||
put(PAYMODE_NONE, PAYMODE_ID_NONE)
|
put(PAYMODE_NONE, PAYMODE_ID_NONE)
|
||||||
put(PAYMODE_CARD, PAYMODE_ID_CARD)
|
put(PAYMODE_CARD, PAYMODE_ID_CARD)
|
||||||
@@ -92,19 +90,19 @@ open class DBBill(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const val CATEGORY_NONE = 0
|
const val CATEGORY_NONE = 0L
|
||||||
const val CATEGORY_GROCERIES = -1
|
const val CATEGORY_GROCERIES = -1L
|
||||||
const val CATEGORY_LEISURE = -2
|
const val CATEGORY_LEISURE = -2L
|
||||||
const val CATEGORY_RENT = -3
|
const val CATEGORY_RENT = -3L
|
||||||
const val CATEGORY_BILLS = -4
|
const val CATEGORY_BILLS = -4L
|
||||||
const val CATEGORY_CULTURE = -5
|
const val CATEGORY_CULTURE = -5L
|
||||||
const val CATEGORY_HEALTH = -6
|
const val CATEGORY_HEALTH = -6L
|
||||||
const val CATEGORY_SHOPPING = -10
|
const val CATEGORY_SHOPPING = -10L
|
||||||
const val CATEGORY_REIMBURSEMENT = -11
|
const val CATEGORY_REIMBURSEMENT = -11L
|
||||||
const val CATEGORY_RESTAURANT = -12
|
const val CATEGORY_RESTAURANT = -12L
|
||||||
const val CATEGORY_ACCOMMODATION = -13
|
const val CATEGORY_ACCOMMODATION = -13L
|
||||||
const val CATEGORY_TRANSPORT = -14
|
const val CATEGORY_TRANSPORT = -14L
|
||||||
const val CATEGORY_SPORT = -15
|
const val CATEGORY_SPORT = -15L
|
||||||
|
|
||||||
const val STATE_OK = 0
|
const val STATE_OK = 0
|
||||||
const val STATE_ADDED = 1
|
const val STATE_ADDED = 1
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ class DBCategory(
|
|||||||
var projectId: Long,
|
var projectId: Long,
|
||||||
var name: String?,
|
var name: String?,
|
||||||
var icon: String,
|
var icon: String,
|
||||||
var color: String
|
var color: String,
|
||||||
|
var state: Int = DBBill.STATE_OK
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ class DBPaymentMode(
|
|||||||
var projectId: Long,
|
var projectId: Long,
|
||||||
var name: String?,
|
var name: String?,
|
||||||
var icon: String,
|
var icon: String,
|
||||||
var color: String
|
var color: String,
|
||||||
|
var state: Int = DBBill.STATE_OK
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ class GroupedBill(
|
|||||||
sourceBills.first().state,
|
sourceBills.first().state,
|
||||||
sourceBills.first().repeat,
|
sourceBills.first().repeat,
|
||||||
sourceBills.first().paymentMode,
|
sourceBills.first().paymentMode,
|
||||||
sourceBills.first().categoryRemoteId,
|
sourceBills.first().categoryId,
|
||||||
sourceBills.first().comment,
|
sourceBills.first().comment,
|
||||||
sourceBills.first().paymentModeRemoteId
|
sourceBills.first().paymentModeId
|
||||||
), Serializable {
|
), Serializable {
|
||||||
init {
|
init {
|
||||||
this.formattedWhat = sourceBills.first().formattedWhat
|
this.formattedWhat = sourceBills.first().formattedWhat
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -265,6 +265,121 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
val members = dbHelper.getMembersOfProject(project.id, null)
|
val members = dbHelper.getMembersOfProject(project.id, null)
|
||||||
val memberIdToRemoteId = members.associate { it.id to it.remoteId }
|
val memberIdToRemoteId = members.associate { it.id to it.remoteId }
|
||||||
|
|
||||||
|
val categoriesToAdd = dbHelper.getCategoriesOfProjectWithState(project.id, DBBill.STATE_ADDED)
|
||||||
|
for (catToAdd in categoriesToAdd) {
|
||||||
|
try {
|
||||||
|
val categoriesResponse = client!!.getCategories(project)
|
||||||
|
val remoteCategories = categoriesResponse.getCategories(project.id)
|
||||||
|
val matchingRemote = remoteCategories.find { it.name == catToAdd.name }
|
||||||
|
|
||||||
|
if (matchingRemote != null) {
|
||||||
|
dbHelper.updateCategory(catToAdd.id, null, null, null, DBBill.STATE_OK, matchingRemote.remoteId)
|
||||||
|
catToAdd.remoteId = matchingRemote.remoteId
|
||||||
|
} else {
|
||||||
|
val createResponse = client!!.createRemoteCategory(project, catToAdd)
|
||||||
|
val newRemoteId = createResponse.remoteCategoryId
|
||||||
|
if (newRemoteId > 0) {
|
||||||
|
dbHelper.updateCategory(catToAdd.id, null, null, null, DBBill.STATE_OK, newRemoteId)
|
||||||
|
catToAdd.remoteId = newRemoteId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "CATEGORY SYNC FAILED for ${catToAdd.name}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val categoriesToEdit = dbHelper.getCategoriesOfProjectWithState(project.id, DBBill.STATE_EDITED)
|
||||||
|
for (catToEdit in categoriesToEdit) {
|
||||||
|
try {
|
||||||
|
client!!.editRemoteCategory(project, catToEdit)
|
||||||
|
dbHelper.updateCategory(catToEdit.id, null, null, null, DBBill.STATE_OK)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "EDIT CATEGORY FAILED for ${catToEdit.name}, might not exist remotely", e)
|
||||||
|
// If it fails, we keep the state as EDITED so it tries again next time,
|
||||||
|
// or we could set it to OK if we think it's a permanent mismatch.
|
||||||
|
// For now just log it.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val categoriesToDelete = dbHelper.getCategoriesOfProjectWithState(project.id, DBBill.STATE_DELETED)
|
||||||
|
for (catToDel in categoriesToDelete) {
|
||||||
|
try {
|
||||||
|
client!!.deleteRemoteCategory(project, catToDel.remoteId)
|
||||||
|
dbHelper.deleteCategory(catToDel.id)
|
||||||
|
} catch (e: NextcloudHttpRequestFailedException) {
|
||||||
|
if (e.statusCode == 404 || e.statusCode == 400) {
|
||||||
|
Log.d(TAG, "failed to delete category on remote project (code ${e.statusCode}) : delete it locally anyway")
|
||||||
|
dbHelper.deleteCategory(catToDel.id)
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "DELETE CATEGORY FAILED for ${catToDel.name}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val paymentModesToAdd = dbHelper.getPaymentModesOfProjectWithState(project.id, DBBill.STATE_ADDED)
|
||||||
|
if (paymentModesToAdd.isNotEmpty()) {
|
||||||
|
try {
|
||||||
|
val pmsResponse = client!!.getPaymentModes(project)
|
||||||
|
val remotePms = pmsResponse.getPaymentModes(project.id)
|
||||||
|
val remotePmsNames = remotePms.map { it.name }
|
||||||
|
for (pmToAdd in paymentModesToAdd) {
|
||||||
|
val searchIndex = remotePmsNames.indexOf(pmToAdd.name)
|
||||||
|
if (searchIndex != -1) {
|
||||||
|
val remotePm = remotePms[searchIndex]
|
||||||
|
dbHelper.updatePaymentMode(pmToAdd.id, null, null, null, DBBill.STATE_OK, remotePm.remoteId)
|
||||||
|
pmToAdd.remoteId = remotePm.remoteId
|
||||||
|
} else {
|
||||||
|
val createRemotePaymentModeResponse = client!!.createRemotePaymentMode(project, pmToAdd)
|
||||||
|
val newRemoteId = createRemotePaymentModeResponse.remotePaymentModeId
|
||||||
|
if (newRemoteId > 0) {
|
||||||
|
dbHelper.updatePaymentMode(pmToAdd.id, null, null, null, DBBill.STATE_OK, newRemoteId)
|
||||||
|
pmToAdd.remoteId = newRemoteId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: NextcloudHttpRequestFailedException) {
|
||||||
|
Log.e(TAG, "GET PAYMENT MODES FAILED : " + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val paymentModesToEdit = dbHelper.getPaymentModesOfProjectWithState(project.id, DBBill.STATE_EDITED)
|
||||||
|
for (pmToEdit in paymentModesToEdit) {
|
||||||
|
try {
|
||||||
|
client!!.editRemotePaymentMode(project, pmToEdit)
|
||||||
|
dbHelper.updatePaymentMode(pmToEdit.id, null, null, null, DBBill.STATE_OK)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "EDIT PAYMENT MODE FAILED for ${pmToEdit.name}, might not exist remotely", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val paymentModesToDelete = dbHelper.getPaymentModesOfProjectWithState(project.id, DBBill.STATE_DELETED)
|
||||||
|
for (pmToDel in paymentModesToDelete) {
|
||||||
|
try {
|
||||||
|
client!!.deleteRemotePaymentMode(project, pmToDel.remoteId)
|
||||||
|
dbHelper.deletePaymentMode(pmToDel.id)
|
||||||
|
} catch (e: NextcloudHttpRequestFailedException) {
|
||||||
|
if (e.statusCode == 404 || e.statusCode == 400) {
|
||||||
|
Log.d(TAG, "failed to delete payment mode on remote project (code ${e.statusCode}) : delete it locally anyway")
|
||||||
|
dbHelper.deletePaymentMode(pmToDel.id)
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val categories = dbHelper.getCategories(project.id)
|
||||||
|
val categoryIdToRemoteId = categories.associate { it.id to it.remoteId }.toMutableMap()
|
||||||
|
// Map hardcoded constants to themselves if not in DB
|
||||||
|
categoryIdToRemoteId[DBBill.CATEGORY_REIMBURSEMENT] = DBBill.CATEGORY_REIMBURSEMENT
|
||||||
|
categories.filter { it.remoteId < 0 }.forEach { categoryIdToRemoteId[it.remoteId] = it.remoteId }
|
||||||
|
|
||||||
|
val paymentModes = dbHelper.getPaymentModes(project.id)
|
||||||
|
val paymentModeIdToRemoteId = paymentModes.associate { it.id to it.remoteId }.toMutableMap()
|
||||||
|
paymentModes.filter { it.remoteId < 0 }.forEach { paymentModeIdToRemoteId[it.remoteId] = it.remoteId }
|
||||||
|
|
||||||
val toDelete = dbHelper.getBillsOfProjectWithState(project.id, DBBill.STATE_DELETED)
|
val toDelete = dbHelper.getBillsOfProjectWithState(project.id, DBBill.STATE_DELETED)
|
||||||
for (bToDel in toDelete) {
|
for (bToDel in toDelete) {
|
||||||
try {
|
try {
|
||||||
@@ -293,12 +408,16 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
val toEdit = dbHelper.getBillsOfProjectWithState(project.id, DBBill.STATE_EDITED)
|
val toEdit = dbHelper.getBillsOfProjectWithState(project.id, DBBill.STATE_EDITED)
|
||||||
for (bToEdit in toEdit) {
|
for (bToEdit in toEdit) {
|
||||||
try {
|
try {
|
||||||
val editRemoteBillResponse = client!!.editRemoteBill(project, bToEdit, memberIdToRemoteId)
|
val editRemoteBillResponse = client!!.editRemoteBill(project, bToEdit, memberIdToRemoteId, categoryIdToRemoteId, paymentModeIdToRemoteId)
|
||||||
if (editRemoteBillResponse.stringContent == bToEdit.remoteId.toString()) {
|
val returnedRemoteId = editRemoteBillResponse.remoteBillId
|
||||||
|
if (returnedRemoteId == bToEdit.remoteId || (returnedRemoteId == 0L && !project.getRequestBaseUrl(true).contains("/ocs/v2.php"))) {
|
||||||
dbHelper.setBillState(bToEdit.id, DBBill.STATE_OK)
|
dbHelper.setBillState(bToEdit.id, DBBill.STATE_OK)
|
||||||
Log.d(TAG, "SUCCESSFUL remote bill edition (${editRemoteBillResponse.stringContent})")
|
Log.d(TAG, "SUCCESSFUL remote bill edition ($returnedRemoteId)")
|
||||||
|
} else if (returnedRemoteId > 0) {
|
||||||
|
dbHelper.setBillState(bToEdit.id, DBBill.STATE_OK)
|
||||||
|
Log.d(TAG, "SUCCESSFUL remote bill edition ($returnedRemoteId)")
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "FAILED to edit remote bill (${editRemoteBillResponse.stringContent})")
|
Log.d(TAG, "FAILED to edit remote bill ($returnedRemoteId)")
|
||||||
}
|
}
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
Log.d(TAG, "FAILED to edit remote bill: it probably does not exist remotely")
|
Log.d(TAG, "FAILED to edit remote bill: it probably does not exist remotely")
|
||||||
@@ -307,8 +426,8 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
|
|
||||||
val toAdd = dbHelper.getBillsOfProjectWithState(project.id, DBBill.STATE_ADDED)
|
val toAdd = dbHelper.getBillsOfProjectWithState(project.id, DBBill.STATE_ADDED)
|
||||||
for (bToAdd in toAdd) {
|
for (bToAdd in toAdd) {
|
||||||
val createRemoteBillResponse = client!!.createRemoteBill(project, bToAdd, memberIdToRemoteId)
|
val createRemoteBillResponse = client!!.createRemoteBill(project, bToAdd, memberIdToRemoteId, categoryIdToRemoteId, paymentModeIdToRemoteId)
|
||||||
val newRemoteId = createRemoteBillResponse.stringContent.toLong()
|
val newRemoteId = createRemoteBillResponse.remoteBillId
|
||||||
if (newRemoteId > 0) {
|
if (newRemoteId > 0) {
|
||||||
dbHelper.updateBill(
|
dbHelper.updateBill(
|
||||||
bToAdd.id, newRemoteId, null,
|
bToAdd.id, newRemoteId, null,
|
||||||
@@ -375,7 +494,7 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
val currencyToAdd = dbHelper.getCurrenciesOfProjectWithState(project.id, DBBill.STATE_ADDED)
|
val currencyToAdd = dbHelper.getCurrenciesOfProjectWithState(project.id, DBBill.STATE_ADDED)
|
||||||
for (cToAdd in currencyToAdd) {
|
for (cToAdd in currencyToAdd) {
|
||||||
val createRemoteCurrencyResponse = client!!.createRemoteCurrency(project, cToAdd)
|
val createRemoteCurrencyResponse = client!!.createRemoteCurrency(project, cToAdd)
|
||||||
val newRemoteId = createRemoteCurrencyResponse.stringContent.toLong()
|
val newRemoteId = createRemoteCurrencyResponse.remoteCurrencyId
|
||||||
if (newRemoteId > 0) {
|
if (newRemoteId > 0) {
|
||||||
dbHelper.setCurrencyState(cToAdd.id, DBBill.STATE_OK)
|
dbHelper.setCurrencyState(cToAdd.id, DBBill.STATE_OK)
|
||||||
}
|
}
|
||||||
@@ -471,7 +590,7 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
|
|
||||||
val localPaymentModes = dbHelper.getPaymentModes(project.id)
|
val localPaymentModes = dbHelper.getPaymentModes(project.id)
|
||||||
for (localPaymentMode in localPaymentModes) {
|
for (localPaymentMode in localPaymentModes) {
|
||||||
if (!remotePaymentModesByRemoteId.containsKey(localPaymentMode.remoteId)) {
|
if (localPaymentMode.state == DBBill.STATE_OK && !remotePaymentModesByRemoteId.containsKey(localPaymentMode.remoteId)) {
|
||||||
dbHelper.deletePaymentMode(localPaymentMode.id)
|
dbHelper.deletePaymentMode(localPaymentMode.id)
|
||||||
Log.d(TAG, "Delete local pm : $localPaymentMode")
|
Log.d(TAG, "Delete local pm : $localPaymentMode")
|
||||||
}
|
}
|
||||||
@@ -481,6 +600,7 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
val remoteCategoriesByRemoteId = remoteCategories.associateBy { it.remoteId }
|
val remoteCategoriesByRemoteId = remoteCategories.associateBy { it.remoteId }
|
||||||
|
|
||||||
for (c in remoteCategories) {
|
for (c in remoteCategories) {
|
||||||
|
if (c.remoteId == DBBill.CATEGORY_REIMBURSEMENT) continue
|
||||||
val localCategory = dbHelper.getCategory(c.remoteId, project.id)
|
val localCategory = dbHelper.getCategory(c.remoteId, project.id)
|
||||||
if (localCategory == null) {
|
if (localCategory == null) {
|
||||||
Log.d(TAG, "Add local category : $c")
|
Log.d(TAG, "Add local category : $c")
|
||||||
@@ -500,7 +620,7 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
|
|
||||||
val localCategories = dbHelper.getCategories(project.id)
|
val localCategories = dbHelper.getCategories(project.id)
|
||||||
for (localCategory in localCategories) {
|
for (localCategory in localCategories) {
|
||||||
if (!remoteCategoriesByRemoteId.containsKey(localCategory.remoteId)) {
|
if (localCategory.state == DBBill.STATE_OK && !remoteCategoriesByRemoteId.containsKey(localCategory.remoteId)) {
|
||||||
dbHelper.deleteCategory(localCategory.id)
|
dbHelper.deleteCategory(localCategory.id)
|
||||||
Log.d(TAG, "Delete local category : $localCategory")
|
Log.d(TAG, "Delete local category : $localCategory")
|
||||||
}
|
}
|
||||||
@@ -528,7 +648,7 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
|
|
||||||
val localCurrencies = dbHelper.getCurrencies(project.id)
|
val localCurrencies = dbHelper.getCurrencies(project.id)
|
||||||
for (localCurrency in localCurrencies) {
|
for (localCurrency in localCurrencies) {
|
||||||
if (!remoteCurrenciesByRemoteId.containsKey(localCurrency.remoteId)) {
|
if (localCurrency.state == DBBill.STATE_OK && !remoteCurrenciesByRemoteId.containsKey(localCurrency.remoteId)) {
|
||||||
dbHelper.deleteCurrency(localCurrency.id)
|
dbHelper.deleteCurrency(localCurrency.id)
|
||||||
Log.d(TAG, "Delete local currency : $localCurrencies")
|
Log.d(TAG, "Delete local currency : $localCurrencies")
|
||||||
}
|
}
|
||||||
@@ -595,13 +715,23 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
val dbMembers = dbHelper.getMembersOfProject(project.id, null)
|
val dbMembers = dbHelper.getMembersOfProject(project.id, null)
|
||||||
val memberRemoteIdToId = dbMembers.associate { it.remoteId to it.id }
|
val memberRemoteIdToId = dbMembers.associate { it.remoteId to it.id }
|
||||||
|
|
||||||
|
val dbCategories = dbHelper.getCategories(project.id)
|
||||||
|
val categoriesRemoteIdToId = dbCategories.associate { it.remoteId to it.id }.toMutableMap()
|
||||||
|
// Map hardcoded constants to their local IDs if they exist in DB, else to themselves
|
||||||
|
categoriesRemoteIdToId[DBBill.CATEGORY_REIMBURSEMENT] = DBBill.CATEGORY_REIMBURSEMENT
|
||||||
|
dbCategories.filter { it.remoteId < 0 }.forEach { categoriesRemoteIdToId[it.remoteId] = it.id }
|
||||||
|
|
||||||
|
val dbPaymentModes = dbHelper.getPaymentModes(project.id)
|
||||||
|
val paymentModesRemoteIdToId = dbPaymentModes.associate { it.remoteId to it.id }.toMutableMap()
|
||||||
|
dbPaymentModes.filter { it.remoteId < 0 }.forEach { paymentModesRemoteIdToId[it.remoteId] = it.id }
|
||||||
|
|
||||||
val billsResponse = client!!.getBills(project)
|
val billsResponse = client!!.getBills(project)
|
||||||
val isIHM = project.type == ProjectType.IHATEMONEY
|
val isIHM = project.type == ProjectType.IHATEMONEY
|
||||||
val serverSyncTimestamp = if (isIHM) 0L else billsResponse.syncTimestamp
|
val serverSyncTimestamp = if (isIHM) 0L else billsResponse.syncTimestamp
|
||||||
val remoteBills: List<DBBill> = if (isIHM) {
|
val remoteBills: List<DBBill> = if (isIHM) {
|
||||||
billsResponse.getBillsIHM(project.id, memberRemoteIdToId)
|
billsResponse.getBillsIHM(project.id, memberRemoteIdToId, categoriesRemoteIdToId, paymentModesRemoteIdToId)
|
||||||
} else {
|
} else {
|
||||||
billsResponse.getBillsCospend(project.id, memberRemoteIdToId)
|
billsResponse.getBillsCospend(project.id, memberRemoteIdToId, categoriesRemoteIdToId, paymentModesRemoteIdToId)
|
||||||
}
|
}
|
||||||
val remoteAllBillIds: List<Long> = if (isIHM) {
|
val remoteAllBillIds: List<Long> = if (isIHM) {
|
||||||
remoteBills.map { it.remoteId }
|
remoteBills.map { it.remoteId }
|
||||||
@@ -618,7 +748,6 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
dbHelper.addBill(remoteBill)
|
dbHelper.addBill(remoteBill)
|
||||||
nbPulledNewBills++
|
nbPulledNewBills++
|
||||||
newBillsDialogText += "+ ${remoteBill.what}\n"
|
newBillsDialogText += "+ ${remoteBill.what}\n"
|
||||||
Log.d(TAG, "Add local bill : $remoteBill")
|
|
||||||
} else {
|
} else {
|
||||||
val localBill = localBillsByRemoteId[remoteBill.remoteId]!!
|
val localBill = localBillsByRemoteId[remoteBill.remoteId]!!
|
||||||
if (hasChanged(localBill, remoteBill)) {
|
if (hasChanged(localBill, remoteBill)) {
|
||||||
@@ -626,12 +755,11 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
localBill.id, null, remoteBill.payerId,
|
localBill.id, null, remoteBill.payerId,
|
||||||
remoteBill.amount, remoteBill.timestamp,
|
remoteBill.amount, remoteBill.timestamp,
|
||||||
remoteBill.what, DBBill.STATE_OK, remoteBill.repeat,
|
remoteBill.what, DBBill.STATE_OK, remoteBill.repeat,
|
||||||
remoteBill.paymentMode, remoteBill.paymentModeRemoteId,
|
remoteBill.paymentMode, remoteBill.paymentModeId,
|
||||||
remoteBill.categoryRemoteId, remoteBill.comment
|
remoteBill.categoryId, remoteBill.comment
|
||||||
)
|
)
|
||||||
nbPulledUpdatedBills++
|
nbPulledUpdatedBills++
|
||||||
updatedBillsDialogText += "✏ ${remoteBill.what}\n"
|
updatedBillsDialogText += "✏ ${remoteBill.what}\n"
|
||||||
Log.d(TAG, "Update local bill : $remoteBill")
|
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "Nothing to do for bill : $localBill")
|
Log.d(TAG, "Nothing to do for bill : $localBill")
|
||||||
}
|
}
|
||||||
@@ -1084,8 +1212,8 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
localBill.what == remoteBill.what &&
|
localBill.what == remoteBill.what &&
|
||||||
localBill.comment == remoteBill.comment &&
|
localBill.comment == remoteBill.comment &&
|
||||||
localBill.paymentMode == remoteBill.paymentMode &&
|
localBill.paymentMode == remoteBill.paymentMode &&
|
||||||
localBill.paymentModeRemoteId == remoteBill.paymentModeRemoteId &&
|
localBill.paymentModeId == remoteBill.paymentModeId &&
|
||||||
localBill.categoryRemoteId == remoteBill.categoryRemoteId
|
localBill.categoryId == remoteBill.categoryId
|
||||||
) {
|
) {
|
||||||
val localRepeat = localBill.repeat ?: DBBill.NON_REPEATED
|
val localRepeat = localBill.repeat ?: DBBill.NON_REPEATED
|
||||||
val remoteRepeat = remoteBill.repeat ?: DBBill.NON_REPEATED
|
val remoteRepeat = remoteBill.repeat ?: DBBill.NON_REPEATED
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ object BillFormatter {
|
|||||||
) {
|
) {
|
||||||
for (bill in bills) {
|
for (bill in bills) {
|
||||||
var whatPrefix = ""
|
var whatPrefix = ""
|
||||||
val pm = paymentModesMap[bill.paymentModeRemoteId.toLong()]
|
val pm = paymentModesMap[bill.paymentModeId]
|
||||||
if (pm != null) {
|
if (pm != null) {
|
||||||
whatPrefix += pm.icon + " "
|
whatPrefix += pm.icon + " "
|
||||||
} else {
|
} else {
|
||||||
when (bill.paymentModeRemoteId) {
|
when (bill.paymentModeId) {
|
||||||
DBBill.PAYMODE_ID_CARD -> whatPrefix += "\uD83D\uDCB3 "
|
DBBill.PAYMODE_ID_CARD -> whatPrefix += "\uD83D\uDCB3 "
|
||||||
DBBill.PAYMODE_ID_CASH -> whatPrefix += "\uD83D\uDCB5 "
|
DBBill.PAYMODE_ID_CASH -> whatPrefix += "\uD83D\uDCB5 "
|
||||||
DBBill.PAYMODE_ID_CHECK -> whatPrefix += "\uD83C\uDFAB "
|
DBBill.PAYMODE_ID_CHECK -> whatPrefix += "\uD83C\uDFAB "
|
||||||
@@ -27,11 +27,11 @@ object BillFormatter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val cat = categoriesMap[bill.categoryRemoteId.toLong()]
|
val cat = categoriesMap[bill.categoryId]
|
||||||
if (cat != null) {
|
if (cat != null) {
|
||||||
whatPrefix += cat.icon + " "
|
whatPrefix += cat.icon + " "
|
||||||
} else {
|
} else {
|
||||||
when (bill.categoryRemoteId) {
|
when (bill.categoryId) {
|
||||||
DBBill.CATEGORY_GROCERIES -> whatPrefix += "\uD83D\uDED2 "
|
DBBill.CATEGORY_GROCERIES -> whatPrefix += "\uD83D\uDED2 "
|
||||||
DBBill.CATEGORY_LEISURE -> whatPrefix += "\uD83C\uDF89 "
|
DBBill.CATEGORY_LEISURE -> whatPrefix += "\uD83C\uDF89 "
|
||||||
DBBill.CATEGORY_RENT -> whatPrefix += "\uD83C\uDFE0 "
|
DBBill.CATEGORY_RENT -> whatPrefix += "\uD83C\uDFE0 "
|
||||||
|
|||||||
@@ -10,28 +10,57 @@ object CategoryUtils {
|
|||||||
|
|
||||||
fun getDefaultCategories(context: Context, projectId: Long): List<DBCategory> {
|
fun getDefaultCategories(context: Context, projectId: Long): List<DBCategory> {
|
||||||
return listOf(
|
return listOf(
|
||||||
DBCategory(0, DBBill.CATEGORY_GROCERIES.toLong(), projectId, context.getString(R.string.category_groceries), "\uD83D\uDED2", "#ffaa00"),
|
DBCategory(DBBill.CATEGORY_GROCERIES,
|
||||||
DBCategory(0, DBBill.CATEGORY_LEISURE.toLong(), projectId, context.getString(R.string.category_leisure), "\uD83C\uDF89", "#aa55ff"),
|
DBBill.CATEGORY_GROCERIES, projectId, context.getString(R.string.category_groceries), "\uD83D\uDED2", "#ffaa00"),
|
||||||
DBCategory(0, DBBill.CATEGORY_RENT.toLong(), projectId, context.getString(R.string.category_rent), "\uD83C\uDFE0", "#da8733"),
|
DBCategory(DBBill.CATEGORY_LEISURE,
|
||||||
DBCategory(0, DBBill.CATEGORY_BILLS.toLong(), projectId, context.getString(R.string.category_bills), "\uD83C\uDF29", "#4aa6b0"),
|
DBBill.CATEGORY_LEISURE, projectId, context.getString(R.string.category_leisure), "\uD83C\uDF89", "#aa55ff"),
|
||||||
DBCategory(0, DBBill.CATEGORY_CULTURE.toLong(), projectId, context.getString(R.string.category_excursion), "\uD83D\uDEB8", "#0055ff"),
|
DBCategory(DBBill.CATEGORY_RENT,
|
||||||
DBCategory(0, DBBill.CATEGORY_HEALTH.toLong(), projectId, context.getString(R.string.category_health), "\uD83D\uDC9A", "#bf090c"),
|
DBBill.CATEGORY_RENT, projectId, context.getString(R.string.category_rent), "\uD83C\uDFE0", "#da8733"),
|
||||||
DBCategory(0, DBBill.CATEGORY_SHOPPING.toLong(), projectId, context.getString(R.string.category_shopping), "\uD83D\uDECD", "#e167d1"),
|
DBCategory(DBBill.CATEGORY_BILLS,
|
||||||
DBCategory(0, DBBill.CATEGORY_REIMBURSEMENT.toLong(), projectId, context.getString(R.string.category_reimbursement), "\uD83D\uDCB0", "#00ced1"),
|
DBBill.CATEGORY_BILLS, projectId, context.getString(R.string.category_bills), "\uD83C\uDF29", "#4aa6b0"),
|
||||||
DBCategory(0, DBBill.CATEGORY_RESTAURANT.toLong(), projectId, context.getString(R.string.category_restaurant), "\uD83C\uDF74", "#d0d5e1"),
|
DBCategory(DBBill.CATEGORY_CULTURE,
|
||||||
DBCategory(0, DBBill.CATEGORY_ACCOMMODATION.toLong(), projectId, context.getString(R.string.category_accomodation), "\uD83D\uDECC", "#5de1a3"),
|
DBBill.CATEGORY_CULTURE, projectId, context.getString(R.string.category_excursion), "\uD83D\uDEB8", "#0055ff"),
|
||||||
DBCategory(0, DBBill.CATEGORY_TRANSPORT.toLong(), projectId, context.getString(R.string.category_transport), "\uD83D\uDE8C", "#6f2ee1"),
|
DBCategory(DBBill.CATEGORY_HEALTH,
|
||||||
DBCategory(0, DBBill.CATEGORY_SPORT.toLong(), projectId, context.getString(R.string.category_sport), "\uD83C\uDFBE", "#69e177")
|
DBBill.CATEGORY_HEALTH, projectId, context.getString(R.string.category_health), "\uD83D\uDC9A", "#bf090c"),
|
||||||
|
DBCategory(DBBill.CATEGORY_SHOPPING,
|
||||||
|
DBBill.CATEGORY_SHOPPING, projectId, context.getString(R.string.category_shopping), "\uD83D\uDECD", "#e167d1"),
|
||||||
|
DBCategory(DBBill.CATEGORY_REIMBURSEMENT,
|
||||||
|
DBBill.CATEGORY_REIMBURSEMENT, projectId, context.getString(R.string.category_reimbursement), "\uD83D\uDCB0", "#00ced1"),
|
||||||
|
DBCategory(DBBill.CATEGORY_RESTAURANT,
|
||||||
|
DBBill.CATEGORY_RESTAURANT, projectId, context.getString(R.string.category_restaurant), "\uD83C\uDF74", "#d0d5e1"),
|
||||||
|
DBCategory(DBBill.CATEGORY_ACCOMMODATION,
|
||||||
|
DBBill.CATEGORY_ACCOMMODATION, projectId, context.getString(R.string.category_accomodation), "\uD83D\uDECC", "#5de1a3"),
|
||||||
|
DBCategory(DBBill.CATEGORY_TRANSPORT,
|
||||||
|
DBBill.CATEGORY_TRANSPORT, projectId, context.getString(R.string.category_transport), "\uD83D\uDE8C", "#6f2ee1"),
|
||||||
|
DBCategory(DBBill.CATEGORY_SPORT,
|
||||||
|
DBBill.CATEGORY_SPORT, projectId, context.getString(R.string.category_sport), "\uD83C\uDFBE", "#69e177")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDefaultPaymentModes(context: Context, projectId: Long): List<DBPaymentMode> {
|
fun getDefaultPaymentModes(context: Context, projectId: Long): List<DBPaymentMode> {
|
||||||
return listOf(
|
return listOf(
|
||||||
DBPaymentMode(0, DBBill.PAYMODE_ID_CARD.toLong(), projectId, context.getString(R.string.payment_mode_credit_card), "\uD83D\uDCB3", "#ff7f50"),
|
DBPaymentMode(DBBill.PAYMODE_ID_CARD, DBBill.PAYMODE_ID_CARD, projectId, context.getString(R.string.payment_mode_credit_card), "\uD83D\uDCB3", "#ff7f50"),
|
||||||
DBPaymentMode(0, DBBill.PAYMODE_ID_CASH.toLong(), projectId, context.getString(R.string.payment_mode_cash), "\uD83D\uDCB5", "#556b2f"),
|
DBPaymentMode(DBBill.PAYMODE_ID_CASH, DBBill.PAYMODE_ID_CASH, projectId, context.getString(R.string.payment_mode_cash), "\uD83D\uDCB5", "#556b2f"),
|
||||||
DBPaymentMode(0, DBBill.PAYMODE_ID_CHECK.toLong(), projectId, context.getString(R.string.payment_mode_check), "\uD83C\uDFAB", "#a9a9a9"),
|
DBPaymentMode(DBBill.PAYMODE_ID_CHECK, DBBill.PAYMODE_ID_CHECK, projectId, context.getString(R.string.payment_mode_check), "\uD83C\uDFAB", "#a9a9a9"),
|
||||||
DBPaymentMode(0, DBBill.PAYMODE_ID_TRANSFER.toLong(), projectId, context.getString(R.string.payment_mode_transfer), "⇄", "#00ced1"),
|
DBPaymentMode(DBBill.PAYMODE_ID_TRANSFER, DBBill.PAYMODE_ID_TRANSFER, projectId, context.getString(R.string.payment_mode_transfer), "⇄", "#00ced1"),
|
||||||
DBPaymentMode(0, DBBill.PAYMODE_ID_ONLINE_SERVICE.toLong(), projectId, context.getString(R.string.payment_mode_online), "\uD83C\uDF0E", "#9932cc")
|
DBPaymentMode(DBBill.PAYMODE_ID_ONLINE_SERVICE, DBBill.PAYMODE_ID_ONLINE_SERVICE, projectId, context.getString(R.string.payment_mode_online), "\uD83C\uDF0E", "#9932cc")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getReimbursementCategoryId(categories: List<DBCategory>, projectId: Long, projectRemoteId: String? = null): Long {
|
||||||
|
return categories.find {
|
||||||
|
it.projectId == projectId && (
|
||||||
|
it.remoteId == DBBill.CATEGORY_REIMBURSEMENT ||
|
||||||
|
(projectRemoteId != null && it.remoteId.toString() == projectRemoteId)
|
||||||
|
)
|
||||||
|
}?.id ?: DBBill.CATEGORY_REIMBURSEMENT
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCategoryById(context: Context, id: Long, projectId: Long = 0): DBCategory? {
|
||||||
|
return getDefaultCategories(context, projectId).find { it.id == id }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPaymentModeById(context: Context, id: Long, projectId: Long = 0): DBPaymentMode? {
|
||||||
|
return getDefaultPaymentModes(context, projectId).find { it.id == id }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ object ExportUtil {
|
|||||||
}
|
}
|
||||||
owersTxt = owersTxt.replace(",$".toRegex(), "")
|
owersTxt = owersTxt.replace(",$".toRegex(), "")
|
||||||
fileContent += "\"${b.what}\",${b.amount},${b.date},${b.timestamp},\"$payerName\"," +
|
fileContent += "\"${b.what}\",${b.amount},${b.date},${b.timestamp},\"$payerName\"," +
|
||||||
"$payerWeight,$payerActive,\"$owersTxt\",${b.repeat},${b.categoryRemoteId}," +
|
"$payerWeight,$payerActive,\"$owersTxt\",${b.repeat},${b.categoryId}," +
|
||||||
"${b.paymentMode}\n"
|
"${b.paymentMode}\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ class NextcloudClient(
|
|||||||
params: Collection<QueryParam>?,
|
params: Collection<QueryParam>?,
|
||||||
isOCSRequest: Boolean
|
isOCSRequest: Boolean
|
||||||
): VersatileProjectSyncClient.ResponseData {
|
): VersatileProjectSyncClient.ResponseData {
|
||||||
|
var finalTarget = target
|
||||||
|
if (finalTarget.contains("/ocs/v2.php") && !finalTarget.contains("format=json")) {
|
||||||
|
finalTarget += if (finalTarget.contains("?")) "&format=json" else "?format=json"
|
||||||
|
}
|
||||||
val result = StringBuilder()
|
val result = StringBuilder()
|
||||||
val headers: MutableMap<String, List<String>> = HashMap()
|
val headers: MutableMap<String, List<String>> = HashMap()
|
||||||
if (isOCSRequest) {
|
if (isOCSRequest) {
|
||||||
@@ -103,13 +107,13 @@ class NextcloudClient(
|
|||||||
val nextcloudRequest: NextcloudRequest = if (params == null) {
|
val nextcloudRequest: NextcloudRequest = if (params == null) {
|
||||||
NextcloudRequest.Builder()
|
NextcloudRequest.Builder()
|
||||||
.setMethod(method)
|
.setMethod(method)
|
||||||
.setUrl(target)
|
.setUrl(finalTarget)
|
||||||
.setHeader(headers)
|
.setHeader(headers)
|
||||||
.build()
|
.build()
|
||||||
} else {
|
} else {
|
||||||
NextcloudRequest.Builder()
|
NextcloudRequest.Builder()
|
||||||
.setMethod(method)
|
.setMethod(method)
|
||||||
.setUrl(target)
|
.setUrl(finalTarget)
|
||||||
.setParameter(params)
|
.setParameter(params)
|
||||||
.setHeader(headers)
|
.setHeader(headers)
|
||||||
.build()
|
.build()
|
||||||
@@ -182,8 +186,12 @@ class NextcloudClient(
|
|||||||
target: String,
|
target: String,
|
||||||
method: String, params: JSONObject?, lastETag: String?, needLogin: Boolean, isOCSRequest: Boolean
|
method: String, params: JSONObject?, lastETag: String?, needLogin: Boolean, isOCSRequest: Boolean
|
||||||
): VersatileProjectSyncClient.ResponseData {
|
): VersatileProjectSyncClient.ResponseData {
|
||||||
|
var finalTarget = target
|
||||||
|
if (finalTarget.contains("/ocs/v2.php") && !finalTarget.contains("format=json")) {
|
||||||
|
finalTarget += if (finalTarget.contains("?")) "&format=json" else "?format=json"
|
||||||
|
}
|
||||||
val result = StringBuilder()
|
val result = StringBuilder()
|
||||||
val targetURL = url + target.replace("^/".toRegex(), "")
|
val targetURL = url + finalTarget.replace("^/".toRegex(), "")
|
||||||
// Log.d(javaClass.simpleName, "method and target URL: $method $targetURL")
|
// Log.d(javaClass.simpleName, "method and target URL: $method $targetURL")
|
||||||
val httpCon = SupportUtil.getHttpURLConnection(targetURL)
|
val httpCon = SupportUtil.getHttpURLConnection(targetURL)
|
||||||
httpCon.requestMethod = method
|
httpCon.requestMethod = method
|
||||||
@@ -194,7 +202,7 @@ class NextcloudClient(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
httpCon.setRequestProperty("Connection", "Close")
|
httpCon.setRequestProperty("Connection", "Close")
|
||||||
httpCon.setRequestProperty("User-Agent", "cowspent-android/" + SupportUtil.getAppVersionName(context))
|
httpCon.setRequestProperty("User-Agent", "Cowspent-android/" + SupportUtil.getAppVersionName(context))
|
||||||
if (lastETag != null && METHOD_GET == method) {
|
if (lastETag != null && METHOD_GET == method) {
|
||||||
httpCon.setRequestProperty("If-None-Match", lastETag)
|
httpCon.setRequestProperty("If-None-Match", lastETag)
|
||||||
}
|
}
|
||||||
@@ -244,8 +252,12 @@ class NextcloudClient(
|
|||||||
target: String,
|
target: String,
|
||||||
method: String, params: JSONObject?, lastETag: String?, needLogin: Boolean, isOCSRequest: Boolean
|
method: String, params: JSONObject?, lastETag: String?, needLogin: Boolean, isOCSRequest: Boolean
|
||||||
): VersatileProjectSyncClient.ResponseData {
|
): VersatileProjectSyncClient.ResponseData {
|
||||||
|
var finalTarget = target
|
||||||
|
if (finalTarget.contains("/ocs/v2.php") && !finalTarget.contains("format=json")) {
|
||||||
|
finalTarget += if (finalTarget.contains("?")) "&format=json" else "?format=json"
|
||||||
|
}
|
||||||
var strBase64: String
|
var strBase64: String
|
||||||
val targetURL = url + target.replace("^/".toRegex(), "")
|
val targetURL = url + finalTarget.replace("^/".toRegex(), "")
|
||||||
val httpCon = SupportUtil.getHttpURLConnection( targetURL)
|
val httpCon = SupportUtil.getHttpURLConnection( targetURL)
|
||||||
httpCon.requestMethod = method
|
httpCon.requestMethod = method
|
||||||
if (needLogin) {
|
if (needLogin) {
|
||||||
|
|||||||
@@ -137,6 +137,90 @@ open class ServerResponse(
|
|||||||
getResponseStringData().toLong()
|
getResponseStringData().toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CreateRemoteCategoryResponse(
|
||||||
|
response: VersatileProjectSyncClient.ResponseData,
|
||||||
|
isOcsResponse: Boolean
|
||||||
|
) : ServerResponse(response, isOcsResponse) {
|
||||||
|
|
||||||
|
@get:Throws(JSONException::class)
|
||||||
|
val stringContent: String
|
||||||
|
get() = getResponseStringData()
|
||||||
|
|
||||||
|
@get:Throws(JSONException::class)
|
||||||
|
val remoteCategoryId: Long
|
||||||
|
get() {
|
||||||
|
val dataStr = getResponseStringData()
|
||||||
|
return try {
|
||||||
|
dataStr.toLong()
|
||||||
|
} catch (_: NumberFormatException) {
|
||||||
|
val obj = JSONObject(dataStr)
|
||||||
|
obj.optLong("id", obj.optLong("remoteId", 0L))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditRemoteCategoryResponse(
|
||||||
|
response: VersatileProjectSyncClient.ResponseData,
|
||||||
|
isOcsResponse: Boolean
|
||||||
|
) : ServerResponse(response, isOcsResponse) {
|
||||||
|
|
||||||
|
@get:Throws(JSONException::class)
|
||||||
|
val stringContent: String
|
||||||
|
get() = getResponseStringData()
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeleteRemoteCategoryResponse(
|
||||||
|
response: VersatileProjectSyncClient.ResponseData,
|
||||||
|
isOcsResponse: Boolean
|
||||||
|
) : ServerResponse(response, isOcsResponse) {
|
||||||
|
|
||||||
|
@get:Throws(JSONException::class)
|
||||||
|
val stringContent: String
|
||||||
|
get() = getResponseStringData()
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreateRemotePaymentModeResponse(
|
||||||
|
response: VersatileProjectSyncClient.ResponseData,
|
||||||
|
isOcsResponse: Boolean
|
||||||
|
) : ServerResponse(response, isOcsResponse) {
|
||||||
|
|
||||||
|
@get:Throws(JSONException::class)
|
||||||
|
val stringContent: String
|
||||||
|
get() = getResponseStringData()
|
||||||
|
|
||||||
|
@get:Throws(JSONException::class)
|
||||||
|
val remotePaymentModeId: Long
|
||||||
|
get() {
|
||||||
|
val dataStr = getResponseStringData()
|
||||||
|
return try {
|
||||||
|
dataStr.toLong()
|
||||||
|
} catch (_: NumberFormatException) {
|
||||||
|
val obj = JSONObject(dataStr)
|
||||||
|
obj.optLong("id", obj.optLong("remoteId", 0L))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditRemotePaymentModeResponse(
|
||||||
|
response: VersatileProjectSyncClient.ResponseData,
|
||||||
|
isOcsResponse: Boolean
|
||||||
|
) : ServerResponse(response, isOcsResponse) {
|
||||||
|
|
||||||
|
@get:Throws(JSONException::class)
|
||||||
|
val stringContent: String
|
||||||
|
get() = getResponseStringData()
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeleteRemotePaymentModeResponse(
|
||||||
|
response: VersatileProjectSyncClient.ResponseData,
|
||||||
|
isOcsResponse: Boolean
|
||||||
|
) : ServerResponse(response, isOcsResponse) {
|
||||||
|
|
||||||
|
@get:Throws(JSONException::class)
|
||||||
|
val stringContent: String
|
||||||
|
get() = getResponseStringData()
|
||||||
|
}
|
||||||
|
|
||||||
class CreateRemoteCurrencyResponse(
|
class CreateRemoteCurrencyResponse(
|
||||||
response: VersatileProjectSyncClient.ResponseData,
|
response: VersatileProjectSyncClient.ResponseData,
|
||||||
isOcsResponse: Boolean
|
isOcsResponse: Boolean
|
||||||
@@ -145,6 +229,18 @@ open class ServerResponse(
|
|||||||
@get:Throws(JSONException::class)
|
@get:Throws(JSONException::class)
|
||||||
val stringContent: String
|
val stringContent: String
|
||||||
get() = getResponseStringData()
|
get() = getResponseStringData()
|
||||||
|
|
||||||
|
@get:Throws(JSONException::class)
|
||||||
|
val remoteCurrencyId: Long
|
||||||
|
get() {
|
||||||
|
val dataStr = getResponseStringData()
|
||||||
|
return try {
|
||||||
|
dataStr.toLong()
|
||||||
|
} catch (_: NumberFormatException) {
|
||||||
|
val obj = JSONObject(dataStr)
|
||||||
|
obj.optLong("id", obj.optLong("remoteId", 0L))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EditRemoteCurrencyResponse(
|
class EditRemoteCurrencyResponse(
|
||||||
@@ -196,6 +292,18 @@ open class ServerResponse(
|
|||||||
@get:Throws(JSONException::class)
|
@get:Throws(JSONException::class)
|
||||||
val stringContent: String
|
val stringContent: String
|
||||||
get() = getResponseStringData()
|
get() = getResponseStringData()
|
||||||
|
|
||||||
|
@get:Throws(JSONException::class)
|
||||||
|
val remoteBillId: Long
|
||||||
|
get() {
|
||||||
|
val dataStr = getResponseStringData()
|
||||||
|
return try {
|
||||||
|
dataStr.toLong()
|
||||||
|
} catch (_: NumberFormatException) {
|
||||||
|
val obj = JSONObject(dataStr)
|
||||||
|
obj.optLong("id", obj.optLong("remoteId", 0L))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateRemoteBillResponse(
|
class CreateRemoteBillResponse(
|
||||||
@@ -206,6 +314,18 @@ open class ServerResponse(
|
|||||||
@get:Throws(JSONException::class)
|
@get:Throws(JSONException::class)
|
||||||
val stringContent: String
|
val stringContent: String
|
||||||
get() = getResponseStringData()
|
get() = getResponseStringData()
|
||||||
|
|
||||||
|
@get:Throws(JSONException::class)
|
||||||
|
val remoteBillId: Long
|
||||||
|
get() {
|
||||||
|
val dataStr = getResponseStringData()
|
||||||
|
return try {
|
||||||
|
dataStr.toLong()
|
||||||
|
} catch (_: NumberFormatException) {
|
||||||
|
val obj = JSONObject(dataStr)
|
||||||
|
obj.optLong("id", obj.optLong("remoteId", 0L))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeleteRemoteBillResponse(
|
class DeleteRemoteBillResponse(
|
||||||
@@ -242,13 +362,23 @@ open class ServerResponse(
|
|||||||
ServerResponse(response, isOcsResponse) {
|
ServerResponse(response, isOcsResponse) {
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun getBillsCospend(projId: Long, memberRemoteIdToId: Map<Long, Long>): List<DBBill> {
|
fun getBillsCospend(
|
||||||
return getBillsFromJSONObject(getResponseObjectData(), projId, memberRemoteIdToId)
|
projId: Long,
|
||||||
|
memberRemoteIdToId: Map<Long, Long>,
|
||||||
|
catRemoteIdToId: Map<Long, Long>,
|
||||||
|
pmRemoteIdToId: Map<Long, Long>
|
||||||
|
): List<DBBill> {
|
||||||
|
return getBillsFromJSONObject(projId, memberRemoteIdToId, catRemoteIdToId, pmRemoteIdToId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun getBillsIHM(projId: Long, memberRemoteIdToId: Map<Long, Long>): List<DBBill> {
|
fun getBillsIHM(
|
||||||
return getBillsFromJSONArray(JSONArray(content), projId, memberRemoteIdToId)
|
projId: Long,
|
||||||
|
memberRemoteIdToId: Map<Long, Long>,
|
||||||
|
catRemoteIdToId: Map<Long, Long>,
|
||||||
|
pmRemoteIdToId: Map<Long, Long>
|
||||||
|
): List<DBBill> {
|
||||||
|
return getBillsFromJSONArray(JSONArray(content), projId, memberRemoteIdToId, catRemoteIdToId, pmRemoteIdToId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -270,6 +400,32 @@ open class ServerResponse(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CategoriesResponse(response: VersatileProjectSyncClient.ResponseData, isOcsResponse: Boolean) :
|
||||||
|
ServerResponse(response, isOcsResponse) {
|
||||||
|
|
||||||
|
@Throws(JSONException::class)
|
||||||
|
fun getCategories(projId: Long): List<DBCategory> {
|
||||||
|
return if (isOcsResponse) {
|
||||||
|
getCategoriesFromJSON(getResponseObjectData(), projId)
|
||||||
|
} else {
|
||||||
|
getCategoriesFromJSONArray(getResponseArrayData(), projId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaymentModesResponse(response: VersatileProjectSyncClient.ResponseData, isOcsResponse: Boolean) :
|
||||||
|
ServerResponse(response, isOcsResponse) {
|
||||||
|
|
||||||
|
@Throws(JSONException::class)
|
||||||
|
fun getPaymentModes(projId: Long): List<DBPaymentMode> {
|
||||||
|
return if (isOcsResponse) {
|
||||||
|
getPaymentModesFromJSON(getResponseObjectData(), projId)
|
||||||
|
} else {
|
||||||
|
getPaymentModesFromJSONArray(getResponseArrayData(), projId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AccountProjectsResponse(
|
class AccountProjectsResponse(
|
||||||
response: VersatileProjectSyncClient.ResponseData,
|
response: VersatileProjectSyncClient.ResponseData,
|
||||||
isOcsResponse: Boolean
|
isOcsResponse: Boolean
|
||||||
@@ -366,6 +522,26 @@ open class ServerResponse(
|
|||||||
return members
|
return members
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(JSONException::class)
|
||||||
|
protected fun getCategoriesFromJSONArray(jsonCats: JSONArray, projId: Long): List<DBCategory> {
|
||||||
|
val categories: MutableList<DBCategory> = ArrayList()
|
||||||
|
for (i in 0 until jsonCats.length()) {
|
||||||
|
val jsonCat = jsonCats.getJSONObject(i)
|
||||||
|
categories.add(getCategoryFromJSON(jsonCat, projId))
|
||||||
|
}
|
||||||
|
return categories
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(JSONException::class)
|
||||||
|
protected fun getPaymentModesFromJSONArray(jsonPms: JSONArray, projId: Long): List<DBPaymentMode> {
|
||||||
|
val paymentModes: MutableList<DBPaymentMode> = ArrayList()
|
||||||
|
for (i in 0 until jsonPms.length()) {
|
||||||
|
val jsonPm = jsonPms.getJSONObject(i)
|
||||||
|
paymentModes.add(getPaymentModeFromJSON(jsonPm, projId))
|
||||||
|
}
|
||||||
|
return paymentModes
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
protected fun getCategoriesFromJSON(json: JSONObject, projId: Long): List<DBCategory> {
|
protected fun getCategoriesFromJSON(json: JSONObject, projId: Long): List<DBCategory> {
|
||||||
val categories: MutableList<DBCategory> = ArrayList()
|
val categories: MutableList<DBCategory> = ArrayList()
|
||||||
@@ -382,6 +558,27 @@ open class ServerResponse(
|
|||||||
return categories
|
return categories
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(JSONException::class)
|
||||||
|
protected fun getCategoryFromJSON(json: JSONObject, projId: Long): DBCategory {
|
||||||
|
var remoteId: Long = 0
|
||||||
|
if (json.has("id") && !json.isNull("id")) {
|
||||||
|
remoteId = json.getLong("id")
|
||||||
|
}
|
||||||
|
var name = ""
|
||||||
|
var color = ""
|
||||||
|
var icon = ""
|
||||||
|
if (json.has("color") && !json.isNull("color")) {
|
||||||
|
color = json.getString("color")
|
||||||
|
}
|
||||||
|
if (json.has("icon") && !json.isNull("icon")) {
|
||||||
|
icon = json.getString("icon")
|
||||||
|
}
|
||||||
|
if (json.has("name") && !json.isNull("name")) {
|
||||||
|
name = json.getString("name")
|
||||||
|
}
|
||||||
|
return DBCategory(0, remoteId, projId, name, icon, color)
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
protected fun getCategoryFromJSON(json: JSONObject, remoteIdStr: String, projId: Long): DBCategory {
|
protected fun getCategoryFromJSON(json: JSONObject, remoteIdStr: String, projId: Long): DBCategory {
|
||||||
val remoteId = remoteIdStr.toLong()
|
val remoteId = remoteIdStr.toLong()
|
||||||
@@ -416,6 +613,27 @@ open class ServerResponse(
|
|||||||
return paymentModes
|
return paymentModes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(JSONException::class)
|
||||||
|
protected fun getPaymentModeFromJSON(json: JSONObject, projId: Long): DBPaymentMode {
|
||||||
|
var remoteId: Long = 0
|
||||||
|
if (json.has("id") && !json.isNull("id")) {
|
||||||
|
remoteId = json.getLong("id")
|
||||||
|
}
|
||||||
|
var name = ""
|
||||||
|
var color = ""
|
||||||
|
var icon = ""
|
||||||
|
if (json.has("color") && !json.isNull("color")) {
|
||||||
|
color = json.getString("color")
|
||||||
|
}
|
||||||
|
if (json.has("icon") && !json.isNull("icon")) {
|
||||||
|
icon = json.getString("icon")
|
||||||
|
}
|
||||||
|
if (json.has("name") && !json.isNull("name")) {
|
||||||
|
name = json.getString("name")
|
||||||
|
}
|
||||||
|
return DBPaymentMode(0, remoteId, projId, name, icon, color)
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
protected fun getPaymentModeFromJSON(json: JSONObject, remoteIdStr: String, projId: Long): DBPaymentMode {
|
protected fun getPaymentModeFromJSON(json: JSONObject, remoteIdStr: String, projId: Long): DBPaymentMode {
|
||||||
val remoteId = remoteIdStr.toLong()
|
val remoteId = remoteIdStr.toLong()
|
||||||
@@ -556,26 +774,30 @@ open class ServerResponse(
|
|||||||
protected fun getBillsFromJSONArray(
|
protected fun getBillsFromJSONArray(
|
||||||
json: JSONArray,
|
json: JSONArray,
|
||||||
projId: Long,
|
projId: Long,
|
||||||
memberRemoteIdToId: Map<Long, Long>
|
memberRemoteIdToId: Map<Long, Long>,
|
||||||
|
catRemoteIdToId: Map<Long, Long>,
|
||||||
|
pmRemoteIdToId: Map<Long, Long>
|
||||||
): List<DBBill> {
|
): List<DBBill> {
|
||||||
val bills: MutableList<DBBill> = ArrayList()
|
val bills: MutableList<DBBill> = ArrayList()
|
||||||
for (i in 0 until json.length()) {
|
for (i in 0 until json.length()) {
|
||||||
val jsonBill = json.getJSONObject(i)
|
val jsonBill = json.getJSONObject(i)
|
||||||
bills.add(getBillFromJSON(jsonBill, projId, memberRemoteIdToId))
|
bills.add(getBillFromJSON(jsonBill, projId, memberRemoteIdToId, catRemoteIdToId, pmRemoteIdToId))
|
||||||
}
|
}
|
||||||
return bills
|
return bills
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
protected fun getBillsFromJSONObject(
|
protected fun getBillsFromJSONObject(
|
||||||
json: JSONObject,
|
|
||||||
projId: Long,
|
projId: Long,
|
||||||
memberRemoteIdToId: Map<Long, Long>
|
memberRemoteIdToId: Map<Long, Long>,
|
||||||
|
catRemoteIdToId: Map<Long, Long>,
|
||||||
|
pmRemoteIdToId: Map<Long, Long>
|
||||||
): List<DBBill> {
|
): List<DBBill> {
|
||||||
val bills: List<DBBill>
|
val bills: List<DBBill>
|
||||||
|
val json = getResponseObjectData()
|
||||||
if (json.has("bills") && !json.isNull("bills")) {
|
if (json.has("bills") && !json.isNull("bills")) {
|
||||||
val jsonBills = json.getJSONArray("bills")
|
val jsonBills = json.getJSONArray("bills")
|
||||||
bills = getBillsFromJSONArray(jsonBills, projId, memberRemoteIdToId)
|
bills = getBillsFromJSONArray(jsonBills, projId, memberRemoteIdToId, catRemoteIdToId, pmRemoteIdToId)
|
||||||
} else {
|
} else {
|
||||||
bills = ArrayList()
|
bills = ArrayList()
|
||||||
}
|
}
|
||||||
@@ -586,7 +808,9 @@ open class ServerResponse(
|
|||||||
protected fun getBillFromJSON(
|
protected fun getBillFromJSON(
|
||||||
json: JSONObject,
|
json: JSONObject,
|
||||||
projId: Long,
|
projId: Long,
|
||||||
memberRemoteIdToId: Map<Long, Long>
|
memberRemoteIdToId: Map<Long, Long>,
|
||||||
|
catRemoteIdToId: Map<Long, Long>,
|
||||||
|
pmRemoteIdToId: Map<Long, Long>
|
||||||
): DBBill {
|
): DBBill {
|
||||||
var remoteId: Long = 0
|
var remoteId: Long = 0
|
||||||
var payerRemoteId: Long
|
var payerRemoteId: Long
|
||||||
@@ -600,7 +824,7 @@ open class ServerResponse(
|
|||||||
var repeat = DBBill.NON_REPEATED
|
var repeat = DBBill.NON_REPEATED
|
||||||
var paymentMode = DBBill.PAYMODE_NONE
|
var paymentMode = DBBill.PAYMODE_NONE
|
||||||
var paymentModeRemoteId = DBBill.PAYMODE_ID_NONE
|
var paymentModeRemoteId = DBBill.PAYMODE_ID_NONE
|
||||||
var categoryId = DBBill.CATEGORY_NONE
|
var categoryRemoteId = DBBill.CATEGORY_NONE
|
||||||
if (!json.isNull("id")) {
|
if (!json.isNull("id")) {
|
||||||
remoteId = json.getLong("id")
|
remoteId = json.getLong("id")
|
||||||
}
|
}
|
||||||
@@ -639,23 +863,29 @@ open class ServerResponse(
|
|||||||
}
|
}
|
||||||
if (json.has("paymentmode") && !json.isNull("paymentmode")) {
|
if (json.has("paymentmode") && !json.isNull("paymentmode")) {
|
||||||
paymentMode = json.getString("paymentmode")
|
paymentMode = json.getString("paymentmode")
|
||||||
|
} else if (json.has("paymentMode") && !json.isNull("paymentMode")) {
|
||||||
|
paymentMode = json.getString("paymentMode")
|
||||||
}
|
}
|
||||||
if (json.has("categoryid") && !json.isNull("categoryid")) {
|
if (json.has("categoryid") && !json.isNull("categoryid")) {
|
||||||
categoryId = json.getInt("categoryid")
|
categoryRemoteId = json.getLong("categoryid")
|
||||||
Log.d("PLOP", "LOADED CATTTTTTTTTTTT $categoryId")
|
} else if (json.has("categoryId") && !json.isNull("categoryId")) {
|
||||||
|
categoryRemoteId = json.getLong("categoryId")
|
||||||
}
|
}
|
||||||
if (json.has("paymentmodeid") && !json.isNull("paymentmodeid")) {
|
if (json.has("paymentmodeid") && !json.isNull("paymentmodeid")) {
|
||||||
paymentModeRemoteId = json.getInt("paymentmodeid")
|
paymentModeRemoteId = json.getLong("paymentmodeid")
|
||||||
|
} else if (json.has("paymentModeId") && !json.isNull("paymentModeId")) {
|
||||||
|
paymentModeRemoteId = json.getLong("paymentModeId")
|
||||||
}
|
}
|
||||||
// old MB, new Cospend is ok as Cospend provides the old pm ID
|
|
||||||
// new MB, old Cospend => set payment mode ID from old one
|
|
||||||
if (DBBill.PAYMODE_NONE != paymentMode && "" != paymentMode && paymentModeRemoteId == DBBill.PAYMODE_ID_NONE) {
|
if (DBBill.PAYMODE_NONE != paymentMode && "" != paymentMode && paymentModeRemoteId == DBBill.PAYMODE_ID_NONE) {
|
||||||
Log.d("PaymentMode", "old: $paymentMode and new: 0")
|
|
||||||
paymentModeRemoteId = DBBill.oldPmIdToNew[paymentMode] ?: DBBill.PAYMODE_ID_NONE
|
paymentModeRemoteId = DBBill.oldPmIdToNew[paymentMode] ?: DBBill.PAYMODE_ID_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val categoryId = catRemoteIdToId[categoryRemoteId] ?: 0L
|
||||||
|
val paymentModeId = pmRemoteIdToId[paymentModeRemoteId] ?: 0L
|
||||||
|
|
||||||
val bill = DBBill(
|
val bill = DBBill(
|
||||||
0, remoteId, projId, payerId, amount, timestamp, what,
|
0, remoteId, projId, payerId, amount, timestamp, what,
|
||||||
DBBill.STATE_OK, repeat, paymentMode, categoryId, comment, paymentModeRemoteId
|
DBBill.STATE_OK, repeat, paymentMode, categoryId, comment, paymentModeId
|
||||||
)
|
)
|
||||||
bill.billOwers = getBillOwersFromJson(json, memberRemoteIdToId)
|
bill.billOwers = getBillOwersFromJson(json, memberRemoteIdToId)
|
||||||
return bill
|
return bill
|
||||||
|
|||||||
@@ -87,14 +87,17 @@ object SupportUtil {
|
|||||||
membersBalance: MutableMap<Long, Double>,
|
membersBalance: MutableMap<Long, Double>,
|
||||||
membersPaid: MutableMap<Long, Double>,
|
membersPaid: MutableMap<Long, Double>,
|
||||||
membersSpent: MutableMap<Long, Double>,
|
membersSpent: MutableMap<Long, Double>,
|
||||||
catId: Int, paymentModeId: Int,
|
catId: Long, paymentModeId: Long,
|
||||||
dateMin: String?, dateMax: String?
|
dateMin: String?, dateMax: String?
|
||||||
): Int {
|
): Int {
|
||||||
|
val proj = db.getProject(projId)
|
||||||
|
val categories = db.getCategories(projId)
|
||||||
|
val reimbursementCategoryId = CategoryUtils.getReimbursementCategoryId(categories, projId, proj?.remoteId)
|
||||||
return getStats(
|
return getStats(
|
||||||
db.getMembersOfProject(projId, null),
|
db.getMembersOfProject(projId, null),
|
||||||
db.getBillsOfProject(projId),
|
db.getBillsOfProject(projId),
|
||||||
membersNbBills, membersBalance, membersPaid, membersSpent,
|
membersNbBills, membersBalance, membersPaid, membersSpent,
|
||||||
catId, paymentModeId, dateMin, dateMax
|
catId, paymentModeId, reimbursementCategoryId, dateMin, dateMax
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +109,8 @@ object SupportUtil {
|
|||||||
membersBalance: MutableMap<Long, Double>,
|
membersBalance: MutableMap<Long, Double>,
|
||||||
membersPaid: MutableMap<Long, Double>,
|
membersPaid: MutableMap<Long, Double>,
|
||||||
membersSpent: MutableMap<Long, Double>,
|
membersSpent: MutableMap<Long, Double>,
|
||||||
catId: Int, paymentModeId: Int,
|
catId: Long, paymentModeId: Long,
|
||||||
|
reimbursementCategoryId: Long,
|
||||||
dateMin: String?, dateMax: String?
|
dateMin: String?, dateMax: String?
|
||||||
): Int {
|
): Int {
|
||||||
val nbBillsTotal = 0
|
val nbBillsTotal = 0
|
||||||
@@ -124,9 +128,9 @@ object SupportUtil {
|
|||||||
for (b in dbBills) {
|
for (b in dbBills) {
|
||||||
// don't take deleted bills and respect category filter
|
// don't take deleted bills and respect category filter
|
||||||
if (b.state != DBBill.STATE_DELETED &&
|
if (b.state != DBBill.STATE_DELETED &&
|
||||||
((catId == -1000 || catId == -100 || b.categoryRemoteId == catId) &&
|
((catId == -1000L || catId == -100L || b.categoryId == catId) &&
|
||||||
(catId != -100 || b.categoryRemoteId != DBBill.CATEGORY_REIMBURSEMENT) &&
|
(catId != -100L || b.categoryId != reimbursementCategoryId) &&
|
||||||
(paymentModeId == -1000 || b.paymentModeRemoteId == paymentModeId)) &&
|
(paymentModeId == -1000L || b.paymentModeId == paymentModeId)) &&
|
||||||
(dateMin == null || b.date >= dateMin) &&
|
(dateMin == null || b.date >= dateMin) &&
|
||||||
(dateMax == null || b.date <= dateMax)
|
(dateMax == null || b.date <= dateMax)
|
||||||
) {
|
) {
|
||||||
@@ -285,18 +289,6 @@ object SupportUtil {
|
|||||||
return reduceBalance(crediters, debiters, results)
|
return reduceBalance(crediters, debiters, results)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun getVersionName(context: Context): String {
|
|
||||||
var versionName = "0.0.0"
|
|
||||||
try {
|
|
||||||
val pInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
|
||||||
versionName = pInfo.versionName ?: "0.0.0"
|
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
return versionName
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getJsonObject(text: String?): JSONObject? {
|
fun getJsonObject(text: String?): JSONObject? {
|
||||||
if (text == null) return null
|
if (text == null) return null
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException
|
|||||||
import com.nextcloud.android.sso.exceptions.TokenMismatchException
|
import com.nextcloud.android.sso.exceptions.TokenMismatchException
|
||||||
import com.nextcloud.android.sso.model.SingleSignOnAccount
|
import com.nextcloud.android.sso.model.SingleSignOnAccount
|
||||||
import net.helcel.cowspent.model.DBBill
|
import net.helcel.cowspent.model.DBBill
|
||||||
|
import net.helcel.cowspent.model.DBCategory
|
||||||
import net.helcel.cowspent.model.DBCurrency
|
import net.helcel.cowspent.model.DBCurrency
|
||||||
import net.helcel.cowspent.model.DBMember
|
import net.helcel.cowspent.model.DBMember
|
||||||
|
import net.helcel.cowspent.model.DBPaymentMode
|
||||||
import net.helcel.cowspent.model.DBProject
|
import net.helcel.cowspent.model.DBProject
|
||||||
import net.helcel.cowspent.model.ProjectType
|
import net.helcel.cowspent.model.ProjectType
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
@@ -82,13 +84,11 @@ class VersatileProjectSyncClient(
|
|||||||
project.getRequestBaseUrl(false) + "/api-priv/projects/" + project.remoteId
|
project.getRequestBaseUrl(false) + "/api-priv/projects/" + project.remoteId
|
||||||
useOcsApiRequest = cospendVersionGT161
|
useOcsApiRequest = cospendVersionGT161
|
||||||
} else if (canAccessProjectWithSSO(project)) {
|
} else if (canAccessProjectWithSSO(project)) {
|
||||||
return if (cospendVersionGT161) {
|
target = if (cospendVersionGT161)
|
||||||
target = "/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId
|
"/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId
|
||||||
ServerResponse.ProjectResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_GET, null, null, true), true)
|
else
|
||||||
} else {
|
"/index.php/apps/cospend/api-priv/projects/" + project.remoteId
|
||||||
target = "/index.php/apps/cospend/api-priv/projects/" + project.remoteId
|
return ServerResponse.ProjectResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_GET, null, null, cospendVersionGT161), cospendVersionGT161)
|
||||||
ServerResponse.ProjectResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_GET, null, null, false), false)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
useOcsApiRequest = cospendVersionGT161
|
useOcsApiRequest = cospendVersionGT161
|
||||||
target = if (cospendVersionGT161)
|
target = if (cospendVersionGT161)
|
||||||
@@ -260,7 +260,13 @@ class VersatileProjectSyncClient(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
fun editRemoteBill(project: DBProject, bill: DBBill, memberIdToRemoteId: Map<Long, Long>): ServerResponse.EditRemoteBillResponse {
|
fun editRemoteBill(
|
||||||
|
project: DBProject,
|
||||||
|
bill: DBBill,
|
||||||
|
memberIdToRemoteId: Map<Long, Long>,
|
||||||
|
categoryIdToRemoteId: Map<Long, Long>,
|
||||||
|
paymentModeIdToRemoteId: Map<Long, Long>
|
||||||
|
): ServerResponse.EditRemoteBillResponse {
|
||||||
val paramKeys: MutableList<String> = ArrayList()
|
val paramKeys: MutableList<String> = ArrayList()
|
||||||
val paramValues: MutableList<String> = ArrayList()
|
val paramValues: MutableList<String> = ArrayList()
|
||||||
paramKeys.add("date")
|
paramKeys.add("date")
|
||||||
@@ -307,8 +313,8 @@ class VersatileProjectSyncClient(
|
|||||||
payedFor = payedFor.replace(",$".toRegex(), "")
|
payedFor = payedFor.replace(",$".toRegex(), "")
|
||||||
paramValues.add(payedFor)
|
paramValues.add(payedFor)
|
||||||
paramValues.add(bill.paymentMode ?: "")
|
paramValues.add(bill.paymentMode ?: "")
|
||||||
paramValues.add(bill.categoryRemoteId.toString())
|
paramValues.add((categoryIdToRemoteId[bill.categoryId] ?: 0L).toString())
|
||||||
paramValues.add(bill.paymentModeRemoteId.toString())
|
paramValues.add((paymentModeIdToRemoteId[bill.paymentModeId] ?: 0L).toString())
|
||||||
|
|
||||||
if (canAccessProjectWithNCLogin(project)) {
|
if (canAccessProjectWithNCLogin(project)) {
|
||||||
username = this.username
|
username = this.username
|
||||||
@@ -512,7 +518,13 @@ class VersatileProjectSyncClient(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
fun createRemoteBill(project: DBProject, bill: DBBill, memberIdToRemoteId: Map<Long, Long>): ServerResponse.CreateRemoteBillResponse {
|
fun createRemoteBill(
|
||||||
|
project: DBProject,
|
||||||
|
bill: DBBill,
|
||||||
|
memberIdToRemoteId: Map<Long, Long>,
|
||||||
|
categoryIdToRemoteId: Map<Long, Long>,
|
||||||
|
paymentModeIdToRemoteId: Map<Long, Long>
|
||||||
|
): ServerResponse.CreateRemoteBillResponse {
|
||||||
val paramKeys: MutableList<String> = ArrayList()
|
val paramKeys: MutableList<String> = ArrayList()
|
||||||
val paramValues: MutableList<String> = ArrayList()
|
val paramValues: MutableList<String> = ArrayList()
|
||||||
paramKeys.add("date")
|
paramKeys.add("date")
|
||||||
@@ -554,8 +566,8 @@ class VersatileProjectSyncClient(
|
|||||||
payedFor = payedFor.replace(",$".toRegex(), "")
|
payedFor = payedFor.replace(",$".toRegex(), "")
|
||||||
paramValues.add(payedFor)
|
paramValues.add(payedFor)
|
||||||
paramValues.add(bill.paymentMode ?: "")
|
paramValues.add(bill.paymentMode ?: "")
|
||||||
paramValues.add(bill.categoryRemoteId.toString())
|
paramValues.add((categoryIdToRemoteId[bill.categoryId] ?: 0L).toString())
|
||||||
paramValues.add(bill.paymentModeRemoteId.toString())
|
paramValues.add((paymentModeIdToRemoteId[bill.paymentModeId] ?: 0L).toString())
|
||||||
|
|
||||||
if (canAccessProjectWithNCLogin(project)) {
|
if (canAccessProjectWithNCLogin(project)) {
|
||||||
username = this.username
|
username = this.username
|
||||||
@@ -634,7 +646,6 @@ class VersatileProjectSyncClient(
|
|||||||
} else if (canAccessProjectWithSSO(project)) {
|
} else if (canAccessProjectWithSSO(project)) {
|
||||||
return if (cospendVersionGT161) {
|
return if (cospendVersionGT161) {
|
||||||
target = "/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId + "/members"
|
target = "/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId + "/members"
|
||||||
Log.i(TAG, "using new API for createRemoteBill")
|
|
||||||
ServerResponse.CreateRemoteMemberResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_POST, paramKeys, paramValues, true), isOcsResponse=true, isJsonMember=true)
|
ServerResponse.CreateRemoteMemberResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_POST, paramKeys, paramValues, true), isOcsResponse=true, isJsonMember=true)
|
||||||
} else {
|
} else {
|
||||||
target = "/index.php/apps/cospend/api-priv/projects/" + project.remoteId + "/members"
|
target = "/index.php/apps/cospend/api-priv/projects/" + project.remoteId + "/members"
|
||||||
@@ -698,7 +709,6 @@ class VersatileProjectSyncClient(
|
|||||||
paramValues.add(tsLastSync.toString())
|
paramValues.add(tsLastSync.toString())
|
||||||
return if (cospendVersionGT161) {
|
return if (cospendVersionGT161) {
|
||||||
target = "/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId + "/bills"
|
target = "/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId + "/bills"
|
||||||
Log.i(TAG, "using new API for getBills")
|
|
||||||
ServerResponse.BillsResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_GET, paramKeys, paramValues, true), true)
|
ServerResponse.BillsResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_GET, paramKeys, paramValues, true), true)
|
||||||
} else {
|
} else {
|
||||||
target = "/index.php/apps/cospend/api-priv/projects/" + project.remoteId + "/bills"
|
target = "/index.php/apps/cospend/api-priv/projects/" + project.remoteId + "/bills"
|
||||||
@@ -733,6 +743,92 @@ class VersatileProjectSyncClient(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(JSONException::class, IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
|
fun getCategories(project: DBProject): ServerResponse.CategoriesResponse {
|
||||||
|
var target: String
|
||||||
|
var username: String? = null
|
||||||
|
var password: String? = null
|
||||||
|
var bearerToken: String? = null
|
||||||
|
var useOcsApiRequest = false
|
||||||
|
if (ProjectType.COSPEND == project.type) {
|
||||||
|
if (canAccessProjectWithNCLogin(project)) {
|
||||||
|
username = this.username
|
||||||
|
password = this.password
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/projects/" + project.remoteId
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api-priv/projects/" + project.remoteId + "/categories"
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
} else if (canAccessProjectWithSSO(project)) {
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
"/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId
|
||||||
|
else
|
||||||
|
"/index.php/apps/cospend/api-priv/projects/" + project.remoteId + "/categories"
|
||||||
|
return ServerResponse.CategoriesResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_GET, null, null, cospendVersionGT161), cospendVersionGT161)
|
||||||
|
} else {
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password)
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/categories"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/categories"
|
||||||
|
username = project.remoteId
|
||||||
|
password = project.password
|
||||||
|
bearerToken = project.bearerToken
|
||||||
|
}
|
||||||
|
return ServerResponse.CategoriesResponse(
|
||||||
|
requestServer(
|
||||||
|
target, METHOD_GET, null, null,
|
||||||
|
null, username, password, bearerToken, useOcsApiRequest
|
||||||
|
), useOcsApiRequest
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(JSONException::class, IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
|
fun getPaymentModes(project: DBProject): ServerResponse.PaymentModesResponse {
|
||||||
|
var target: String
|
||||||
|
var username: String? = null
|
||||||
|
var password: String? = null
|
||||||
|
var bearerToken: String? = null
|
||||||
|
var useOcsApiRequest = false
|
||||||
|
if (ProjectType.COSPEND == project.type) {
|
||||||
|
if (canAccessProjectWithNCLogin(project)) {
|
||||||
|
username = this.username
|
||||||
|
password = this.password
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/projects/" + project.remoteId
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api-priv/projects/" + project.remoteId + "/paymentmodes"
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
} else if (canAccessProjectWithSSO(project)) {
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
"/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId
|
||||||
|
else
|
||||||
|
"/index.php/apps/cospend/api-priv/projects/" + project.remoteId + "/paymentmodes"
|
||||||
|
return ServerResponse.PaymentModesResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_GET, null, null, cospendVersionGT161), cospendVersionGT161)
|
||||||
|
} else {
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password)
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/paymentmodes"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/paymentmodes"
|
||||||
|
username = project.remoteId
|
||||||
|
password = project.password
|
||||||
|
bearerToken = project.bearerToken
|
||||||
|
}
|
||||||
|
return ServerResponse.PaymentModesResponse(
|
||||||
|
requestServer(
|
||||||
|
target, METHOD_GET, null, null,
|
||||||
|
null, username, password, bearerToken, useOcsApiRequest
|
||||||
|
), useOcsApiRequest
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(JSONException::class, IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
@Throws(JSONException::class, IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
fun getMembers(project: DBProject): ServerResponse.MembersResponse {
|
fun getMembers(project: DBProject): ServerResponse.MembersResponse {
|
||||||
var target: String
|
var target: String
|
||||||
@@ -779,6 +875,305 @@ class VersatileProjectSyncClient(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
|
fun createRemoteCategory(project: DBProject, category: DBCategory): ServerResponse.CreateRemoteCategoryResponse {
|
||||||
|
val paramKeys: MutableList<String> = ArrayList()
|
||||||
|
val paramValues: MutableList<String> = ArrayList()
|
||||||
|
paramKeys.add("name")
|
||||||
|
paramValues.add(category.name ?: "")
|
||||||
|
paramKeys.add("icon")
|
||||||
|
paramValues.add(category.icon)
|
||||||
|
paramKeys.add("color")
|
||||||
|
paramValues.add(category.color)
|
||||||
|
|
||||||
|
var target: String
|
||||||
|
var username: String? = null
|
||||||
|
var password: String? = null
|
||||||
|
var bearerToken: String? = null
|
||||||
|
var useOcsApiRequest = false
|
||||||
|
if (ProjectType.COSPEND == project.type) {
|
||||||
|
if (canAccessProjectWithNCLogin(project)) {
|
||||||
|
username = this.username
|
||||||
|
password = this.password
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/projects/" + project.remoteId + "/category"
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api-priv/projects/" + project.remoteId + "/categories"
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
} else if (canAccessProjectWithSSO(project)) {
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
"/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId + "/category"
|
||||||
|
else
|
||||||
|
"/index.php/apps/cospend/api-priv/projects/" + project.remoteId + "/categories"
|
||||||
|
return ServerResponse.CreateRemoteCategoryResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_POST, paramKeys, paramValues, cospendVersionGT161), cospendVersionGT161)
|
||||||
|
} else {
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/category"
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/categories"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/categories"
|
||||||
|
username = project.remoteId
|
||||||
|
password = project.password
|
||||||
|
bearerToken = project.bearerToken
|
||||||
|
}
|
||||||
|
|
||||||
|
val response = requestServer(
|
||||||
|
target, METHOD_POST, paramKeys, paramValues, null,
|
||||||
|
username, password, bearerToken, useOcsApiRequest
|
||||||
|
)
|
||||||
|
return ServerResponse.CreateRemoteCategoryResponse(response, useOcsApiRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
|
fun editRemoteCategory(project: DBProject, category: DBCategory): ServerResponse.EditRemoteCategoryResponse {
|
||||||
|
val paramKeys: MutableList<String> = ArrayList()
|
||||||
|
val paramValues: MutableList<String> = ArrayList()
|
||||||
|
paramKeys.add("name")
|
||||||
|
paramValues.add(category.name ?: "")
|
||||||
|
paramKeys.add("icon")
|
||||||
|
paramValues.add(category.icon)
|
||||||
|
paramKeys.add("color")
|
||||||
|
paramValues.add(category.color)
|
||||||
|
|
||||||
|
var target: String
|
||||||
|
var username: String? = null
|
||||||
|
var password: String? = null
|
||||||
|
var bearerToken: String? = null
|
||||||
|
var useOcsApiRequest = false
|
||||||
|
if (ProjectType.COSPEND == project.type) {
|
||||||
|
if (canAccessProjectWithNCLogin(project)) {
|
||||||
|
username = this.username
|
||||||
|
password = this.password
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/projects/" + project.remoteId + "/category/" + category.remoteId
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api-priv/projects/" + project.remoteId + "/categories/" + category.remoteId
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
} else if (canAccessProjectWithSSO(project)) {
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
"/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId + "/category/" + category.remoteId
|
||||||
|
else
|
||||||
|
"/index.php/apps/cospend/api-priv/projects/" + project.remoteId + "/categories/" + category.remoteId
|
||||||
|
return ServerResponse.EditRemoteCategoryResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_PUT, paramKeys, paramValues, cospendVersionGT161), cospendVersionGT161)
|
||||||
|
} else {
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/category/" + category.remoteId
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/categories/" + category.remoteId
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/categories/" + category.remoteId
|
||||||
|
username = project.remoteId
|
||||||
|
password = project.password
|
||||||
|
bearerToken = project.bearerToken
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerResponse.EditRemoteCategoryResponse(
|
||||||
|
requestServer(
|
||||||
|
target, METHOD_PUT, paramKeys, paramValues, null,
|
||||||
|
username, password, bearerToken, useOcsApiRequest
|
||||||
|
), useOcsApiRequest
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
|
fun deleteRemoteCategory(project: DBProject, categoryRemoteId: Long): ServerResponse.DeleteRemoteCategoryResponse {
|
||||||
|
var target: String
|
||||||
|
var username: String? = null
|
||||||
|
var password: String? = null
|
||||||
|
var bearerToken: String? = null
|
||||||
|
var useOcsApiRequest = false
|
||||||
|
if (ProjectType.COSPEND == project.type) {
|
||||||
|
if (canAccessProjectWithNCLogin(project)) {
|
||||||
|
username = this.username
|
||||||
|
password = this.password
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/projects/" + project.remoteId + "/category/" + categoryRemoteId
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api-priv/projects/" + project.remoteId + "/categories/" + categoryRemoteId
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
} else if (canAccessProjectWithSSO(project)) {
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
"/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId + "/category/" + categoryRemoteId
|
||||||
|
else
|
||||||
|
"/index.php/apps/cospend/api-priv/projects/" + project.remoteId + "/categories/" + categoryRemoteId
|
||||||
|
return ServerResponse.DeleteRemoteCategoryResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_DELETE, null, null, cospendVersionGT161), cospendVersionGT161)
|
||||||
|
} else {
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/category/" + categoryRemoteId
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/categories/" + categoryRemoteId
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/categories/" + categoryRemoteId
|
||||||
|
username = project.remoteId
|
||||||
|
password = project.password
|
||||||
|
bearerToken = project.bearerToken
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerResponse.DeleteRemoteCategoryResponse(
|
||||||
|
requestServer(
|
||||||
|
target, METHOD_DELETE, null, null,
|
||||||
|
null, username, password, bearerToken, useOcsApiRequest
|
||||||
|
), useOcsApiRequest
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
|
fun createRemotePaymentMode(project: DBProject, paymentMode: DBPaymentMode): ServerResponse.CreateRemotePaymentModeResponse {
|
||||||
|
val paramKeys: MutableList<String> = ArrayList()
|
||||||
|
val paramValues: MutableList<String> = ArrayList()
|
||||||
|
paramKeys.add("name")
|
||||||
|
paramValues.add(paymentMode.name ?: "")
|
||||||
|
paramKeys.add("icon")
|
||||||
|
paramValues.add(paymentMode.icon)
|
||||||
|
paramKeys.add("color")
|
||||||
|
paramValues.add(paymentMode.color)
|
||||||
|
|
||||||
|
var target: String
|
||||||
|
var username: String? = null
|
||||||
|
var password: String? = null
|
||||||
|
var bearerToken: String? = null
|
||||||
|
var useOcsApiRequest = false
|
||||||
|
if (ProjectType.COSPEND == project.type) {
|
||||||
|
if (canAccessProjectWithNCLogin(project)) {
|
||||||
|
username = this.username
|
||||||
|
password = this.password
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/projects/" + project.remoteId + "/paymentmode"
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api-priv/projects/" + project.remoteId + "/paymentmodes"
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
} else if (canAccessProjectWithSSO(project)) {
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
"/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId + "/paymentmode"
|
||||||
|
else
|
||||||
|
"/index.php/apps/cospend/api-priv/projects/" + project.remoteId + "/paymentmodes"
|
||||||
|
return ServerResponse.CreateRemotePaymentModeResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_POST, paramKeys, paramValues, cospendVersionGT161), cospendVersionGT161)
|
||||||
|
} else {
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/paymentmode"
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/paymentmodes"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/paymentmodes"
|
||||||
|
username = project.remoteId
|
||||||
|
password = project.password
|
||||||
|
bearerToken = project.bearerToken
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerResponse.CreateRemotePaymentModeResponse(
|
||||||
|
requestServer(
|
||||||
|
target, METHOD_POST, paramKeys, paramValues, null,
|
||||||
|
username, password, bearerToken, useOcsApiRequest
|
||||||
|
), useOcsApiRequest
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
|
fun editRemotePaymentMode(project: DBProject, paymentMode: DBPaymentMode): ServerResponse.EditRemotePaymentModeResponse {
|
||||||
|
val paramKeys: MutableList<String> = ArrayList()
|
||||||
|
val paramValues: MutableList<String> = ArrayList()
|
||||||
|
paramKeys.add("name")
|
||||||
|
paramValues.add(paymentMode.name ?: "")
|
||||||
|
paramKeys.add("icon")
|
||||||
|
paramValues.add(paymentMode.icon)
|
||||||
|
paramKeys.add("color")
|
||||||
|
paramValues.add(paymentMode.color)
|
||||||
|
|
||||||
|
var target: String
|
||||||
|
var username: String? = null
|
||||||
|
var password: String? = null
|
||||||
|
var bearerToken: String? = null
|
||||||
|
var useOcsApiRequest = false
|
||||||
|
if (ProjectType.COSPEND == project.type) {
|
||||||
|
if (canAccessProjectWithNCLogin(project)) {
|
||||||
|
username = this.username
|
||||||
|
password = this.password
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/projects/" + project.remoteId + "/paymentmode/" + paymentMode.remoteId
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api-priv/projects/" + project.remoteId + "/paymentmodes/" + paymentMode.remoteId
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
} else if (canAccessProjectWithSSO(project)) {
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
"/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId + "/paymentmode/" + paymentMode.remoteId
|
||||||
|
else
|
||||||
|
"/index.php/apps/cospend/api-priv/projects/" + project.remoteId + "/paymentmodes/" + paymentMode.remoteId
|
||||||
|
return ServerResponse.EditRemotePaymentModeResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_PUT, paramKeys, paramValues, cospendVersionGT161), cospendVersionGT161)
|
||||||
|
} else {
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/paymentmode/" + paymentMode.remoteId
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/paymentmodes/" + paymentMode.remoteId
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/paymentmodes/" + paymentMode.remoteId
|
||||||
|
username = project.remoteId
|
||||||
|
password = project.password
|
||||||
|
bearerToken = project.bearerToken
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerResponse.EditRemotePaymentModeResponse(
|
||||||
|
requestServer(
|
||||||
|
target, METHOD_PUT, paramKeys, paramValues, null,
|
||||||
|
username, password, bearerToken, useOcsApiRequest
|
||||||
|
), useOcsApiRequest
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
|
fun deleteRemotePaymentMode(project: DBProject, paymentModeRemoteId: Long): ServerResponse.DeleteRemotePaymentModeResponse {
|
||||||
|
var target: String
|
||||||
|
var username: String? = null
|
||||||
|
var password: String? = null
|
||||||
|
var bearerToken: String? = null
|
||||||
|
var useOcsApiRequest = false
|
||||||
|
if (ProjectType.COSPEND == project.type) {
|
||||||
|
if (canAccessProjectWithNCLogin(project)) {
|
||||||
|
username = this.username
|
||||||
|
password = this.password
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/projects/" + project.remoteId + "/paymentmode/" + paymentModeRemoteId
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api-priv/projects/" + project.remoteId + "/paymentmodes/" + paymentModeRemoteId
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
} else if (canAccessProjectWithSSO(project)) {
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
"/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId + "/paymentmode/" + paymentModeRemoteId
|
||||||
|
else
|
||||||
|
"/index.php/apps/cospend/api-priv/projects/" + project.remoteId + "/paymentmodes/" + paymentModeRemoteId
|
||||||
|
return ServerResponse.DeleteRemotePaymentModeResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_DELETE, null, null, cospendVersionGT161), cospendVersionGT161)
|
||||||
|
} else {
|
||||||
|
useOcsApiRequest = cospendVersionGT161
|
||||||
|
target = if (cospendVersionGT161)
|
||||||
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/paymentmode/" + paymentModeRemoteId
|
||||||
|
else
|
||||||
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/paymentmodes/" + paymentModeRemoteId
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/paymentmodes/" + paymentModeRemoteId
|
||||||
|
username = project.remoteId
|
||||||
|
password = project.password
|
||||||
|
bearerToken = project.bearerToken
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerResponse.DeleteRemotePaymentModeResponse(
|
||||||
|
requestServer(
|
||||||
|
target, METHOD_DELETE, null, null,
|
||||||
|
null, username, password, bearerToken, useOcsApiRequest
|
||||||
|
), useOcsApiRequest
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
fun createRemoteCurrency(project: DBProject, currency: DBCurrency): ServerResponse.CreateRemoteCurrencyResponse {
|
fun createRemoteCurrency(project: DBProject, currency: DBCurrency): ServerResponse.CreateRemoteCurrencyResponse {
|
||||||
val paramKeys: MutableList<String> = ArrayList()
|
val paramKeys: MutableList<String> = ArrayList()
|
||||||
@@ -936,6 +1331,10 @@ class VersatileProjectSyncClient(
|
|||||||
nextcloudAPI: NextcloudAPI, target: String, method: String,
|
nextcloudAPI: NextcloudAPI, target: String, method: String,
|
||||||
paramKeys: List<String>?, paramValues: List<String>?, isOCSRequest: Boolean
|
paramKeys: List<String>?, paramValues: List<String>?, isOCSRequest: Boolean
|
||||||
): ResponseData {
|
): ResponseData {
|
||||||
|
var finalTarget = target
|
||||||
|
if (finalTarget.contains("/ocs/v2.php") && !finalTarget.contains("format=json")) {
|
||||||
|
finalTarget += if (finalTarget.contains("?")) "&format=json" else "?format=json"
|
||||||
|
}
|
||||||
val result = StringBuilder()
|
val result = StringBuilder()
|
||||||
var params: MutableList<QueryParam>? = null
|
var params: MutableList<QueryParam>? = null
|
||||||
if (paramKeys != null && paramValues != null) {
|
if (paramKeys != null && paramValues != null) {
|
||||||
@@ -953,13 +1352,13 @@ class VersatileProjectSyncClient(
|
|||||||
val nextcloudRequest: NextcloudRequest = if (params == null) {
|
val nextcloudRequest: NextcloudRequest = if (params == null) {
|
||||||
NextcloudRequest.Builder()
|
NextcloudRequest.Builder()
|
||||||
.setMethod(method)
|
.setMethod(method)
|
||||||
.setUrl(target)
|
.setUrl(finalTarget)
|
||||||
.setHeader(headers)
|
.setHeader(headers)
|
||||||
.build()
|
.build()
|
||||||
} else {
|
} else {
|
||||||
NextcloudRequest.Builder()
|
NextcloudRequest.Builder()
|
||||||
.setMethod(method)
|
.setMethod(method)
|
||||||
.setUrl(target)
|
.setUrl(finalTarget)
|
||||||
.setParameter(params)
|
.setParameter(params)
|
||||||
.setHeader(headers)
|
.setHeader(headers)
|
||||||
.build()
|
.build()
|
||||||
@@ -993,8 +1392,12 @@ class VersatileProjectSyncClient(
|
|||||||
lastETag: String?, username: String?, password: String?,
|
lastETag: String?, username: String?, password: String?,
|
||||||
bearerToken: String?, isOCSRequest: Boolean
|
bearerToken: String?, isOCSRequest: Boolean
|
||||||
): ResponseData {
|
): ResponseData {
|
||||||
|
var finalTarget = target
|
||||||
|
if (finalTarget.contains("/ocs/v2.php") && !finalTarget.contains("format=json")) {
|
||||||
|
finalTarget += if (finalTarget.contains("?")) "&format=json" else "?format=json"
|
||||||
|
}
|
||||||
val result = StringBuilder()
|
val result = StringBuilder()
|
||||||
val httpCon = SupportUtil.getHttpURLConnection(target)
|
val httpCon = SupportUtil.getHttpURLConnection(finalTarget)
|
||||||
httpCon.requestMethod = method
|
httpCon.requestMethod = method
|
||||||
if (bearerToken != null) {
|
if (bearerToken != null) {
|
||||||
httpCon.setRequestProperty("Authorization", "Bearer $bearerToken")
|
httpCon.setRequestProperty("Authorization", "Bearer $bearerToken")
|
||||||
@@ -1005,7 +1408,7 @@ class VersatileProjectSyncClient(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
httpCon.setRequestProperty("Connection", "Close")
|
httpCon.setRequestProperty("Connection", "Close")
|
||||||
httpCon.setRequestProperty("User-Agent", "Cowspent/" + SupportUtil.getAppVersionName(context))
|
httpCon.setRequestProperty("User-Agent", "Cowspent-android/" + SupportUtil.getAppVersionName(context))
|
||||||
if (lastETag != null && METHOD_GET == method) {
|
if (lastETag != null && METHOD_GET == method) {
|
||||||
httpCon.setRequestProperty("If-None-Match", lastETag)
|
httpCon.setRequestProperty("If-None-Match", lastETag)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@
|
|||||||
<string name="title_settle">Settle Project</string>
|
<string name="title_settle">Settle Project</string>
|
||||||
<string name="title_share">Share Project</string>
|
<string name="title_share">Share Project</string>
|
||||||
<string name="title_add_project">Add Project</string>
|
<string name="title_add_project">Add Project</string>
|
||||||
|
<string name="title_add_category">Add Category</string>
|
||||||
|
<string name="title_add_payment_mode">Add Payment Mode</string>
|
||||||
<string name="title_account">Nextcloud Account</string>
|
<string name="title_account">Nextcloud Account</string>
|
||||||
<string name="title_share_web">Web link</string>
|
<string name="title_share_web">Web link</string>
|
||||||
<string name="title_share_qr">Cowspent link</string>
|
<string name="title_share_qr">Cowspent link</string>
|
||||||
@@ -133,6 +135,8 @@
|
|||||||
<string name="settings_color_custom">Custom color</string>
|
<string name="settings_color_custom">Custom color</string>
|
||||||
<string name="settings_color_mode">Color Selection</string>
|
<string name="settings_color_mode">Color Selection</string>
|
||||||
<string name="settings_show_archived">Show archived projects</string>
|
<string name="settings_show_archived">Show archived projects</string>
|
||||||
|
<string name="settings_beta_features">Beta Features</string>
|
||||||
|
<string name="settings_beta_features_summary">Enable experimental features. Use at your own risk.</string>
|
||||||
<string name="settings_url_warn_http">WARNING: "http" is unsafe. Use "https".</string>
|
<string name="settings_url_warn_http">WARNING: "http" is unsafe. Use "https".</string>
|
||||||
<string name="settings_colorpicker_title">Choose Color</string>
|
<string name="settings_colorpicker_title">Choose Color</string>
|
||||||
|
|
||||||
@@ -153,6 +157,7 @@
|
|||||||
<string name="pref_key_color_mode" translatable="false">colorMode</string>
|
<string name="pref_key_color_mode" translatable="false">colorMode</string>
|
||||||
<string name="pref_key_offline_mode" translatable="false">offlineMode</string>
|
<string name="pref_key_offline_mode" translatable="false">offlineMode</string>
|
||||||
<string name="pref_key_show_archived" translatable="false">showArchived</string>
|
<string name="pref_key_show_archived" translatable="false">showArchived</string>
|
||||||
|
<string name="pref_key_beta_features" translatable="false">betaFeatures</string>
|
||||||
<string name="pref_value_night_mode_no" translatable="false">1</string>
|
<string name="pref_value_night_mode_no" translatable="false">1</string>
|
||||||
<string name="pref_value_night_mode_yes" translatable="false">2</string>
|
<string name="pref_value_night_mode_yes" translatable="false">2</string>
|
||||||
<string name="pref_value_night_mode_system" translatable="false">-1</string>
|
<string name="pref_value_night_mode_system" translatable="false">-1</string>
|
||||||
@@ -229,7 +234,7 @@
|
|||||||
<string name="settle_bill_what">Settlement</string>
|
<string name="settle_bill_what">Settlement</string>
|
||||||
|
|
||||||
<!-- Currencies -->
|
<!-- Currencies -->
|
||||||
<string name="currency_dialog_title">Choose Currency</string>
|
<string name="currency_dialog_title">Choose Currency (%s)</string>
|
||||||
<string name="setting_none">None</string>
|
<string name="setting_none">None</string>
|
||||||
<string name="setting_all">All</string>
|
<string name="setting_all">All</string>
|
||||||
<string name="currency_saved_success">Currency settings saved.</string>
|
<string name="currency_saved_success">Currency settings saved.</string>
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ This means you can choose where your data is going and preserve your privacy.
|
|||||||
* Dark theme and customizable main app color
|
* Dark theme and customizable main app color
|
||||||
* Share/import projects with link/QRCode
|
* Share/import projects with link/QRCode
|
||||||
* Connect to a Nextcloud account to automatically add projects
|
* Connect to a Nextcloud account to automatically add projects
|
||||||
* Background sync service with notifications on bills events
|
|
||||||
* Multi-lingual user-interface (translated on Crowdin: https://crowdin.com/project/cowspent)
|
|
||||||
|
|
||||||
# Requirements
|
# Requirements
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user