package space.luminic.finance.services import kotlinx.coroutines.reactive.awaitSingle import org.bson.Document import org.bson.types.ObjectId 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.unwind import org.springframework.data.mongodb.core.aggregation.AggregationOperation import org.springframework.data.mongodb.core.aggregation.ConvertOperators import org.springframework.data.mongodb.core.aggregation.LookupOperation import org.springframework.data.mongodb.core.query.Criteria import org.springframework.stereotype.Service import space.luminic.finance.dtos.AccountDTO import space.luminic.finance.models.Account import space.luminic.finance.models.Transaction import space.luminic.finance.repos.AccountRepo @Service class AccountServiceImpl( private val accountRepo: AccountRepo, private val mongoTemplate: ReactiveMongoTemplate, private val spaceService: SpaceService, private val transactionService: TransactionService ): AccountService { private fun basicAggregation(spaceId: String): List { val addFieldsAsOJ = addFields() .addField("createdByOI") .withValue(ConvertOperators.valueOf("createdById").convertToObjectId()) .addField("updatedByOI") .withValue(ConvertOperators.valueOf("updatedById").convertToObjectId()) .build() 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() matchCriteria.add(Criteria.where("spaceId").`is`(spaceId)) matchCriteria.add(Criteria.where("isDeleted").`is`(false)) val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray())) return listOf(addFieldsAsOJ, lookupCreatedBy, unwindCreatedBy, lookupUpdatedBy, unwindUpdatedBy, matchStage) } override suspend fun getAccounts(spaceId: String): List { val basicAggregation = basicAggregation(spaceId) val aggregation = newAggregation(*basicAggregation.toTypedArray()) return mongoTemplate.aggregate(aggregation, "accounts", Account::class.java) .collectList() .awaitSingle() } override suspend fun getAccount( spaceId: String, accountId: String ): Account { val basicAggregation = basicAggregation(spaceId) val matchStage = match (Criteria.where("_id").`is`(ObjectId(accountId))) val aggregation = newAggregation(matchStage, *basicAggregation.toTypedArray()) return mongoTemplate.aggregate(aggregation, "accounts", Account::class.java) .awaitSingle() } override suspend fun getAccountTransactions( spaceId: String, accountId: String ): List { val space = spaceService.checkSpace(spaceId) val filter = TransactionService.TransactionsFilter( accountId = accountId, ) return transactionService.getTransactions(spaceId, filter, "date", "ASC") } override suspend fun createAccount( spaceId: String, account: AccountDTO.CreateAccountDTO ): Account { val createdAccount = Account( type = account.type, spaceId = spaceId, name = account.name, currencyCode = account.currencyCode, amount = account.amount, goalId = account.goalId, ) return accountRepo.save(createdAccount).awaitSingle() } override suspend fun updateAccount( spaceId: String, account: AccountDTO.UpdateAccountDTO ): Account { val existingAccount = getAccount(spaceId, account.id) val newAccount = existingAccount.copy( name = account.name, type = account.type, currencyCode = account.currencyCode, amount = account.amount, goalId = account.goalId, ) return accountRepo.save(newAccount).awaitSingle() } override suspend fun deleteAccount(spaceId: String, accountId: String) { val existingAccount = getAccount(spaceId, accountId) existingAccount.isDeleted = true accountRepo.save(existingAccount).awaitSingle() } }