init
This commit is contained in:
@@ -1,20 +1,5 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||
import kotlinx.coroutines.reactive.awaitSingle
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.data.domain.Sort.Direction
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.addFields
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.lookup
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.match
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.sort
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.unwind
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperation
|
||||
import org.springframework.data.mongodb.core.aggregation.ConvertOperators
|
||||
import org.springframework.data.mongodb.core.query.Criteria
|
||||
import org.springframework.stereotype.Service
|
||||
import space.luminic.finance.dtos.TransactionDTO
|
||||
import space.luminic.finance.models.NotFoundException
|
||||
@@ -23,163 +8,89 @@ import space.luminic.finance.repos.TransactionRepo
|
||||
|
||||
@Service
|
||||
class TransactionServiceImpl(
|
||||
private val mongoTemplate: ReactiveMongoTemplate,
|
||||
private val transactionRepo: TransactionRepo,
|
||||
private val spaceService: SpaceService,
|
||||
private val categoryService: CategoryService,
|
||||
private val transactionRepo: TransactionRepo,
|
||||
private val authService: AuthService,
|
||||
) : TransactionService {
|
||||
|
||||
|
||||
private fun basicAggregation(spaceId: String): List<AggregationOperation> {
|
||||
val addFieldsOI = addFields()
|
||||
.addField("createdByOI")
|
||||
.withValue(ConvertOperators.valueOf("createdById").convertToObjectId())
|
||||
.addField("updatedByOI")
|
||||
.withValue(ConvertOperators.valueOf("updatedById").convertToObjectId())
|
||||
.addField("fromAccountIdOI")
|
||||
.withValue(ConvertOperators.valueOf("fromAccountId").convertToObjectId())
|
||||
.addField("toAccountIdOI")
|
||||
.withValue(ConvertOperators.valueOf("toAccountId").convertToObjectId())
|
||||
.addField("categoryIdOI")
|
||||
.withValue(ConvertOperators.valueOf("categoryId").convertToObjectId())
|
||||
.build()
|
||||
|
||||
val lookupFromAccount = lookup("accounts", "fromAccountIdOI", "_id", "fromAccount")
|
||||
val unwindFromAccount = unwind("fromAccount")
|
||||
val lookupToAccount = lookup("accounts", "toAccountIdOI", "_id", "toAccount")
|
||||
val unwindToAccount = unwind("toAccount", true)
|
||||
|
||||
val lookupCategory = lookup("categories", "categoryIdOI", "_id", "category")
|
||||
val unwindCategory = unwind("category")
|
||||
|
||||
val lookupCreatedBy = lookup("users", "createdByOI", "_id", "createdBy")
|
||||
val unwindCreatedBy = unwind("createdBy")
|
||||
|
||||
val lookupUpdatedBy = lookup("users", "updatedByOI", "_id", "updatedBy")
|
||||
val unwindUpdatedBy = unwind("updatedBy")
|
||||
val matchCriteria = mutableListOf<Criteria>()
|
||||
matchCriteria.add(Criteria.where("spaceId").`is`(spaceId))
|
||||
val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
|
||||
return listOf(
|
||||
matchStage,
|
||||
addFieldsOI,
|
||||
lookupFromAccount,
|
||||
unwindFromAccount,
|
||||
lookupToAccount,
|
||||
unwindToAccount,
|
||||
lookupCategory,
|
||||
unwindCategory,
|
||||
lookupCreatedBy,
|
||||
unwindCreatedBy,
|
||||
lookupUpdatedBy,
|
||||
unwindUpdatedBy
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getTransactions(
|
||||
spaceId: String,
|
||||
override fun getTransactions(
|
||||
spaceId: Int,
|
||||
filter: TransactionService.TransactionsFilter,
|
||||
sortBy: String,
|
||||
sortDirection: String
|
||||
): List<Transaction> {
|
||||
val allowedSortFields = setOf("date", "amount", "category.name", "createdAt")
|
||||
require(sortBy in allowedSortFields) { "Invalid sort field: $sortBy" }
|
||||
|
||||
val direction = when (sortDirection.uppercase()) {
|
||||
"ASC" -> Direction.ASC
|
||||
"DESC" -> Direction.DESC
|
||||
else -> throw IllegalArgumentException("Sort direction must be 'ASC' or 'DESC'")
|
||||
}
|
||||
val basicAggregation = basicAggregation(spaceId)
|
||||
|
||||
val sort = sort(Sort.by(direction, sortBy))
|
||||
val matchCriteria = mutableListOf<Criteria>()
|
||||
filter.dateFrom?.let { matchCriteria.add(Criteria.where("date").gte(it)) }
|
||||
filter.dateTo?.let { matchCriteria.add(Criteria.where("date").lte(it)) }
|
||||
val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
val aggregation =
|
||||
newAggregation(
|
||||
matchStage,
|
||||
*basicAggregation.toTypedArray(),
|
||||
sort
|
||||
)
|
||||
|
||||
return mongoTemplate.aggregate(aggregation, "transactions", Transaction::class.java)
|
||||
.collectList()
|
||||
.awaitSingle()
|
||||
return transactionRepo.findAllBySpaceId(spaceId)
|
||||
}
|
||||
|
||||
override suspend fun getTransaction(
|
||||
spaceId: String,
|
||||
transactionId: String
|
||||
override fun getTransaction(
|
||||
spaceId: Int,
|
||||
transactionId: Int
|
||||
): Transaction {
|
||||
val matchCriteria = mutableListOf<Criteria>()
|
||||
matchCriteria.add(Criteria.where("spaceId").`is`(spaceId))
|
||||
matchCriteria.add(Criteria.where("_id").`is`(ObjectId(transactionId)))
|
||||
val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
|
||||
val aggregation =
|
||||
newAggregation(
|
||||
matchStage,
|
||||
)
|
||||
return mongoTemplate.aggregate(aggregation, "transactions", Transaction::class.java)
|
||||
.awaitFirstOrNull() ?: throw NotFoundException("Transaction with ID $transactionId not found")
|
||||
spaceService.getSpace(spaceId)
|
||||
return transactionRepo.findBySpaceIdAndId(spaceId, transactionId)
|
||||
?: throw NotFoundException("Transaction with id $transactionId not found")
|
||||
}
|
||||
|
||||
override suspend fun createTransaction(
|
||||
spaceId: String,
|
||||
override fun createTransaction(
|
||||
spaceId: Int,
|
||||
transaction: TransactionDTO.CreateTransactionDTO
|
||||
): Transaction {
|
||||
if (transaction.type == Transaction.TransactionType.TRANSFER && transaction.toAccountId == null) {
|
||||
throw IllegalArgumentException("Cannot create a transaction with type TRANSFER without a toAccountId")
|
||||
}
|
||||
): Int {
|
||||
val userId = authService.getSecurityUserId()
|
||||
val space = spaceService.getSpace(spaceId)
|
||||
val category = categoryService.getCategory(spaceId, transaction.categoryId)
|
||||
if (transaction.type != Transaction.TransactionType.TRANSFER && transaction.type.name != category.type.name) {
|
||||
throw IllegalArgumentException("Transaction type should match with category type")
|
||||
}
|
||||
val transaction = Transaction(
|
||||
spaceId = spaceId,
|
||||
space = space,
|
||||
type = transaction.type,
|
||||
kind = transaction.kind,
|
||||
categoryId = transaction.categoryId,
|
||||
category = category,
|
||||
comment = transaction.comment,
|
||||
amount = transaction.amount,
|
||||
fees = transaction.fees,
|
||||
date = transaction.date,
|
||||
fromAccountId = transaction.fromAccountId,
|
||||
toAccountId = transaction.toAccountId,
|
||||
)
|
||||
return transactionRepo.save(transaction).awaitSingle()
|
||||
return transactionRepo.create(transaction, userId)
|
||||
}
|
||||
|
||||
override suspend fun updateTransaction(
|
||||
spaceId: String,
|
||||
override fun updateTransaction(
|
||||
spaceId: Int,
|
||||
transactionId: Int,
|
||||
transaction: TransactionDTO.UpdateTransactionDTO
|
||||
): Transaction {
|
||||
if (transaction.type == Transaction.TransactionType.TRANSFER && transaction.toAccountId == null) {
|
||||
throw IllegalArgumentException("Cannot edit a transaction with type TRANSFER without a toAccountId")
|
||||
}
|
||||
val exitingTx = getTransaction(spaceId, transaction.id)
|
||||
val transaction = exitingTx.copy(
|
||||
spaceId = exitingTx.spaceId,
|
||||
): Int {
|
||||
val space = spaceService.getSpace(spaceId)
|
||||
val existingTransaction = getTransaction(space.id!!, transactionId)
|
||||
val newCategory = categoryService.getCategory(spaceId, transaction.categoryId)
|
||||
// val id: Int,
|
||||
// val type: TransactionType = TransactionType.EXPENSE,
|
||||
// val kind: TransactionKind = TransactionKind.INSTANT,
|
||||
// val category: Int,
|
||||
// val comment: String,
|
||||
// val amount: BigDecimal,
|
||||
// val fees: BigDecimal = BigDecimal.ZERO,
|
||||
// val date: Instant
|
||||
val updatedTransaction = Transaction(
|
||||
id = existingTransaction.id,
|
||||
space = existingTransaction.space,
|
||||
parent = existingTransaction.parent,
|
||||
type = transaction.type,
|
||||
kind = transaction.kind,
|
||||
categoryId = transaction.category,
|
||||
category = newCategory,
|
||||
comment = transaction.comment,
|
||||
amount = transaction.amount,
|
||||
fees = transaction.fees,
|
||||
date = transaction.date,
|
||||
fromAccountId = transaction.fromAccountId,
|
||||
toAccountId = transaction.toAccountId,
|
||||
isDeleted = existingTransaction.isDeleted,
|
||||
isDone = transaction.isDone,
|
||||
createdBy = existingTransaction.createdBy,
|
||||
createdAt = existingTransaction.createdAt
|
||||
|
||||
)
|
||||
return transactionRepo.save(transaction).awaitSingle()
|
||||
return transactionRepo.update(updatedTransaction)
|
||||
}
|
||||
|
||||
override suspend fun deleteTransaction(spaceId: String, transactionId: String) {
|
||||
val transaction = getTransaction(spaceId, transactionId)
|
||||
transaction.isDeleted = true
|
||||
transactionRepo.save(transaction).awaitSingle()
|
||||
override fun deleteTransaction(spaceId: Int, transactionId: Int) {
|
||||
val space = spaceService.getSpace(spaceId)
|
||||
getTransaction(space.id!!, transactionId)
|
||||
transactionRepo.delete(transactionId)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user