ارتقا به نسخه 1.1.4 و نمایش جزئیات و تاریخ پرداخت در لیست پرداختشدهها
Android Release / build (push) Canceled after 0s
Android Release / build (push) Canceled after 0s
This commit is contained in:
1 parent
ee0bd0ba2b
commit
1931e20deb
7 files changed
+86
-37
No files matched your search
@@ -30,8 +30,8 @@ android {
|
||||
applicationId = "com.oqba26.monthlypaymentapp"
|
||||
minSdk = 25
|
||||
targetSdk = 34
|
||||
versionCode = 14
|
||||
versionName = "1.1.3"
|
||||
versionCode = 15
|
||||
versionName = "1.1.4"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables { useSupportLibrary = true }
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ fun PaidListScreen(
|
||||
PersonListItem(
|
||||
person = person,
|
||||
index = index + 1,
|
||||
listType = PersonListType.PAID,
|
||||
onPersonClick = { navController.navigate("person_detail/${person.id}") },
|
||||
onQuickPayClick = { },
|
||||
onArchiveClick = { personToArchive.value = it },
|
||||
|
||||
@@ -1332,6 +1332,7 @@ fun PersonListItem(
|
||||
person: PersonUiModel,
|
||||
index: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
listType: PersonListType = PersonListType.UNPAID,
|
||||
isSelectionMode: Boolean = false,
|
||||
isSelected: Boolean = false,
|
||||
reorderableState: org.burnoutcrew.reorderable.ReorderableLazyListState? = null,
|
||||
@@ -1368,6 +1369,15 @@ fun PersonListItem(
|
||||
)
|
||||
}
|
||||
|
||||
if (listType == PersonListType.PAID && person.hasPaidThisMonth) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Check,
|
||||
contentDescription = "Paid",
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(20.dp).padding(end = 2.dp)
|
||||
)
|
||||
}
|
||||
|
||||
val dragModifier = if (reorderableState != null && !isSelectionMode) {
|
||||
Modifier.detectReorderAfterLongPress(reorderableState)
|
||||
} else Modifier
|
||||
@@ -1377,14 +1387,33 @@ fun PersonListItem(
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = dragModifier.padding(end = 4.dp)
|
||||
)
|
||||
Text(
|
||||
person.name,
|
||||
modifier = Modifier.weight(1f),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Visible
|
||||
)
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
person.name,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Visible
|
||||
)
|
||||
if (listType == PersonListType.PAID) {
|
||||
person.lastPaymentTime?.let { time ->
|
||||
Text(
|
||||
text = formatTimestampToPersianDateTime(time),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
person.lastPaymentAmount?.let { amount ->
|
||||
Text(
|
||||
text = "مبلغ: ${formatNumberAsPersian(amount)} تومان",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (person.debtCount > 0) {
|
||||
Surface(
|
||||
@@ -1420,29 +1449,31 @@ fun PersonListItem(
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.width(4.dp))
|
||||
if (listType != PersonListType.PAID) {
|
||||
Spacer(Modifier.width(4.dp))
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
if (person.hasPaidThisMonth) {
|
||||
Icon(Icons.Filled.Check, contentDescription = "Paid", tint = MaterialTheme.colorScheme.primary)
|
||||
} else {
|
||||
IconButton(onClick = { onQuickPayClick(person) }, modifier = Modifier.size(32.dp)) {
|
||||
Icon(Icons.Filled.Payments, contentDescription = "Pay", tint = MaterialTheme.colorScheme.primary)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
if (person.hasPaidThisMonth) {
|
||||
Icon(Icons.Filled.Check, contentDescription = "Paid", tint = MaterialTheme.colorScheme.primary)
|
||||
} else {
|
||||
IconButton(onClick = { onQuickPayClick(person) }, modifier = Modifier.size(32.dp)) {
|
||||
Icon(Icons.Filled.Payments, contentDescription = "Pay", tint = MaterialTheme.colorScheme.primary)
|
||||
}
|
||||
}
|
||||
}
|
||||
IconButton(onClick = { onArchiveClick(person) }, modifier = Modifier.size(32.dp)) {
|
||||
Icon(Icons.Filled.Archive, contentDescription = "Archive", tint = MaterialTheme.colorScheme.secondary)
|
||||
}
|
||||
if (person.debtCount > 0) {
|
||||
IconButton(onClick = { onSmsClick(person) }, modifier = Modifier.size(32.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.Message,
|
||||
contentDescription = "SMS",
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.graphicsLayer(scaleX = -1f)
|
||||
)
|
||||
IconButton(onClick = { onArchiveClick(person) }, modifier = Modifier.size(32.dp)) {
|
||||
Icon(Icons.Filled.Archive, contentDescription = "Archive", tint = MaterialTheme.colorScheme.secondary)
|
||||
}
|
||||
if (person.debtCount > 0) {
|
||||
IconButton(onClick = { onSmsClick(person) }, modifier = Modifier.size(32.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.Message,
|
||||
contentDescription = "SMS",
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.graphicsLayer(scaleX = -1f)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,11 @@ fun formatTimestampToPersianDateTime(timestamp: Long): String {
|
||||
return "$date، ساعت $time"
|
||||
}
|
||||
|
||||
fun isTimestampInCurrentCycle(timestamp: Long): Boolean {
|
||||
fun isTimestampInCurrentCycle(rawTimestamp: Long): Boolean {
|
||||
if (rawTimestamp <= 0L) return false
|
||||
|
||||
val timestamp = if (rawTimestamp < 100000000000L) rawTimestamp * 1000L else rawTimestamp
|
||||
|
||||
val now = PersianDate()
|
||||
val curYear = now.shYear
|
||||
val curMonth = now.shMonth
|
||||
|
||||
@@ -198,6 +198,8 @@ class PersonViewModel @Inject constructor(
|
||||
it.shamsiMonth == targetMonth && it.shamsiYear == targetYear && it.category == category
|
||||
}
|
||||
|
||||
val categoryPayments = allPayments.filter { it.category == category }
|
||||
|
||||
val activePersons = persons
|
||||
.filter { !it.isArchived && it.category == category }
|
||||
.sortedWith(compareBy({ it.displayOrder }, { it.createdAt }))
|
||||
@@ -205,6 +207,13 @@ class PersonViewModel @Inject constructor(
|
||||
val uiModels = activePersons.map { person ->
|
||||
val hasPaid = relevantPayments.any { it.personId == person.id }
|
||||
|
||||
val personCyclePayments = categoryPayments.filter {
|
||||
it.personId == person.id && isTimestampInCurrentCycle(if ((it.createdAt ?: 0L) > 0L) it.createdAt!! else it.timestamp)
|
||||
}
|
||||
val latestPayment = personCyclePayments.maxByOrNull { if ((it.createdAt ?: 0L) > 0L) it.createdAt!! else it.timestamp }
|
||||
val lastPaymentTime = latestPayment?.let { if ((it.createdAt ?: 0L) > 0L) it.createdAt!! else it.timestamp }
|
||||
val lastPaymentAmount = latestPayment?.amount
|
||||
|
||||
val unpaidResult = if (category == "mosque" || person.isAnonymous) {
|
||||
Triple(0, 0.0, emptyList<String>() to emptyList())
|
||||
} else {
|
||||
@@ -242,14 +251,16 @@ class PersonViewModel @Inject constructor(
|
||||
monthlyCommitment = person.monthlyCommitment,
|
||||
unpaidMonthsNames = unpaidMonthsNames,
|
||||
unpaidMonths = unpaidMonths,
|
||||
needsSync = person.needsSync
|
||||
needsSync = person.needsSync,
|
||||
lastPaymentTime = lastPaymentTime,
|
||||
lastPaymentAmount = lastPaymentAmount
|
||||
)
|
||||
}
|
||||
|
||||
val categoryPayments = allPayments.filter { it.category == category }
|
||||
updateDashboard(uiModels.size, relevantPayments, categoryPayments)
|
||||
|
||||
val (paidModels, unpaidModels) = uiModels.partition { it.hasPaidThisMonth }
|
||||
val (rawPaidModels, unpaidModels) = uiModels.partition { it.hasPaidThisMonth }
|
||||
val paidModels = rawPaidModels.sortedByDescending { it.lastPaymentTime ?: 0L }
|
||||
|
||||
// محاسبه گزارشات مالی جامع
|
||||
val personsMap = persons.associateBy { it.id }
|
||||
@@ -596,7 +607,7 @@ class PersonViewModel @Inject constructor(
|
||||
val paidCount = paymentsThisMonth.map { it.personId }.distinct().size
|
||||
val totalIncome = paymentsThisMonth.sumOf { it.amount }
|
||||
val cyclePayments = categoryPayments.filter { payment ->
|
||||
val time = payment.createdAt ?: payment.timestamp
|
||||
val time = if ((payment.createdAt ?: 0L) > 0L) payment.createdAt!! else payment.timestamp
|
||||
isTimestampInCurrentCycle(time)
|
||||
}
|
||||
val totalCycleIncome = cyclePayments.sumOf { it.amount }
|
||||
|
||||
@@ -17,6 +17,8 @@ data class PersonUiModel(
|
||||
val unpaidMonthsNames: List<String> = emptyList(),
|
||||
val unpaidMonths: List<Int> = emptyList(),
|
||||
val needsSync: Boolean = false,
|
||||
val lastPaymentTime: Long? = null,
|
||||
val lastPaymentAmount: Double? = null
|
||||
)
|
||||
|
||||
data class PersonListUiState(
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
org.gradle.jvmargs=-Xmx2560m -XX:+UseParallelGC -Dfile.encoding=UTF-8
|
||||
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.enableJetifier=false
|
||||
kotlin.code.style=official
|
||||
android.nonTransitiveRClass=true
|
||||
android.suppressUnsupportedCompileSdk=34
|
||||
|
||||
Reference in new issue
Block a user