add spaces
This commit is contained in:
@@ -8,6 +8,7 @@ import org.springframework.cache.annotation.CacheEvict
|
||||
import org.springframework.cache.annotation.Cacheable
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.data.domain.Sort.Direction
|
||||
import org.springframework.data.mongodb.core.MongoTemplate
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.*
|
||||
import org.springframework.data.mongodb.core.aggregation.DateOperators.DateToString
|
||||
@@ -19,6 +20,7 @@ import reactor.core.publisher.Flux
|
||||
import reactor.core.publisher.Mono
|
||||
import space.luminic.budgerapp.models.*
|
||||
import space.luminic.budgerapp.repos.BudgetRepo
|
||||
import space.luminic.budgerapp.repos.CategoryRepo
|
||||
import space.luminic.budgerapp.repos.TransactionRepo
|
||||
import space.luminic.budgerapp.repos.WarnRepo
|
||||
import java.time.*
|
||||
@@ -32,7 +34,9 @@ class FinancialService(
|
||||
val transactionsRepo: TransactionRepo,
|
||||
val recurrentService: RecurrentService,
|
||||
val userService: UserService,
|
||||
val reactiveMongoTemplate: ReactiveMongoTemplate
|
||||
val reactiveMongoTemplate: ReactiveMongoTemplate,
|
||||
private val spaceService: SpaceService,
|
||||
private val categoryRepo: CategoryRepo
|
||||
) {
|
||||
private val logger = LoggerFactory.getLogger(FinancialService::class.java)
|
||||
|
||||
@@ -191,75 +195,108 @@ class FinancialService(
|
||||
}.then() // Возвращаем корректный Mono<Void>
|
||||
}
|
||||
|
||||
@Cacheable("budgetsList")
|
||||
fun getBudgets(sortSetting: SortSetting? = null): Mono<MutableList<Budget>> {
|
||||
val sort = if (sortSetting != null) {
|
||||
Sort.by(sortSetting.order, sortSetting.by)
|
||||
} else {
|
||||
Sort.by(Sort.Direction.DESC, "dateFrom")
|
||||
}
|
||||
fun getBudgets(spaceId: String, sortSetting: SortSetting? = null): Mono<List<Budget>> {
|
||||
val sort = sortSetting?.let {
|
||||
Sort.by(it.order, it.by)
|
||||
} ?: Sort.by(Sort.Direction.DESC, "dateFrom")
|
||||
|
||||
return budgetRepo.findAll(sort)
|
||||
.collectList() // Сбор Flux<Budget> в Mono<List<Budget>>
|
||||
return ReactiveSecurityContextHolder.getContext()
|
||||
.map { it.authentication }
|
||||
.flatMap { authentication ->
|
||||
val username = authentication.name
|
||||
spaceService.getSpace(spaceId)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space not found for $spaceId")))
|
||||
.flatMap { space ->
|
||||
userService.getByUsername(username)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
||||
.flatMap { user ->
|
||||
val userIds = space.users.mapNotNull { it.id?.toString() }
|
||||
if (user.id !in userIds) {
|
||||
Mono.error(IllegalArgumentException("User cannot access this Space"))
|
||||
} else {
|
||||
val spaceObjectId = try {
|
||||
ObjectId(space.id!!) // Преобразуем строку в ObjectId
|
||||
} catch (e: IllegalArgumentException) {
|
||||
return@flatMap Mono.error(IllegalArgumentException("Invalid Space ID format: ${space.id}"))
|
||||
}
|
||||
|
||||
println("Space ID type: ${spaceObjectId::class.java}, value: $spaceObjectId")
|
||||
// Применяем сортировку к запросу
|
||||
budgetRepo.findBySpaceId(spaceObjectId, sort).collectList()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @Cacheable("budgets", key = "#id")
|
||||
fun getBudget(id: String): Mono<BudgetDTO> {
|
||||
return budgetRepo.findById(id)
|
||||
.flatMap { budget ->
|
||||
val budgetDTO = BudgetDTO(
|
||||
budget.id,
|
||||
budget.name,
|
||||
budget.dateFrom,
|
||||
budget.dateTo,
|
||||
budget.createdAt,
|
||||
categories = budget.categories,
|
||||
incomeCategories = budget.incomeCategories,
|
||||
)
|
||||
return ReactiveSecurityContextHolder.getContext()
|
||||
.flatMap { securityContext ->
|
||||
val username = securityContext.authentication.name
|
||||
budgetRepo.findById(id)
|
||||
.flatMap { budget ->
|
||||
// Проверяем, что пользователь есть в space бюджета
|
||||
if (!budget.space!!.users.any { it.username == username }) {
|
||||
return@flatMap Mono.error(IllegalArgumentException("User does not have access to this space"))
|
||||
}
|
||||
|
||||
logger.info("Fetching categories and transactions")
|
||||
val categoriesMono = getBudgetCategories(budgetDTO.dateFrom, budgetDTO.dateTo)
|
||||
val transactionsMono =
|
||||
getTransactionsByTypes(budgetDTO.dateFrom, budgetDTO.dateTo)
|
||||
// Если доступ есть, продолжаем процесс
|
||||
val budgetDTO = BudgetDTO(
|
||||
budget.id,
|
||||
budget.space,
|
||||
budget.name,
|
||||
budget.dateFrom,
|
||||
budget.dateTo,
|
||||
budget.createdAt,
|
||||
categories = budget.categories,
|
||||
incomeCategories = budget.incomeCategories,
|
||||
)
|
||||
|
||||
logger.info("Fetching categories and transactions")
|
||||
val categoriesMono = getBudgetCategories(budgetDTO.dateFrom, budgetDTO.dateTo)
|
||||
val transactionsMono = getTransactionsByTypes(budgetDTO.dateFrom, budgetDTO.dateTo)
|
||||
|
||||
Mono.zip(categoriesMono, transactionsMono)
|
||||
.flatMap { tuple ->
|
||||
val categories = tuple.t1
|
||||
val transactions = tuple.t2
|
||||
Mono.zip(categoriesMono, transactionsMono)
|
||||
.flatMap { tuple ->
|
||||
val categories = tuple.t1
|
||||
val transactions = tuple.t2
|
||||
|
||||
Flux.fromIterable(budgetDTO.categories)
|
||||
.map { category ->
|
||||
categories[category.category.id]?.let { data ->
|
||||
category.currentSpent = data["instantAmount"] ?: 0.0
|
||||
category.currentPlanned = data["plannedAmount"] ?: 0.0
|
||||
}
|
||||
category
|
||||
}
|
||||
.collectList()
|
||||
.map { updatedCategories ->
|
||||
budgetDTO.categories = updatedCategories
|
||||
budgetDTO.plannedExpenses = transactions["plannedExpenses"] as MutableList
|
||||
budgetDTO.plannedIncomes = transactions["plannedIncomes"] as MutableList
|
||||
budgetDTO.transactions = transactions["instantTransactions"] as MutableList
|
||||
|
||||
Flux.fromIterable(budgetDTO.categories)
|
||||
.map { category ->
|
||||
categories[category.category.id]?.let { data ->
|
||||
category.currentSpent = data["instantAmount"] ?: 0.0
|
||||
category.currentPlanned = data["plannedAmount"] ?: 0.0
|
||||
}
|
||||
category
|
||||
}
|
||||
.collectList()
|
||||
.map { updatedCategories ->
|
||||
budgetDTO.categories = updatedCategories
|
||||
budgetDTO.plannedExpenses = transactions["plannedExpenses"] as MutableList
|
||||
budgetDTO.plannedIncomes = transactions["plannedIncomes"] as MutableList
|
||||
budgetDTO.transactions = transactions["instantTransactions"] as MutableList
|
||||
|
||||
budgetDTO
|
||||
budgetDTO
|
||||
}
|
||||
}
|
||||
}
|
||||
.doOnError { error ->
|
||||
logger.error("Error fetching budget: ${error.message}", error)
|
||||
}
|
||||
.switchIfEmpty(Mono.error(BudgetNotFoundException("Budget not found with id: $id")))
|
||||
}
|
||||
.doOnError { error ->
|
||||
logger.error("Error fetching budget: ${error.message}", error)
|
||||
}
|
||||
.switchIfEmpty(Mono.error(BudgetNotFoundException("Budget not found with id: $id")))
|
||||
}
|
||||
|
||||
fun regenCats(): Mono<Void> {
|
||||
|
||||
fun regenBudgets(): Mono<Void> {
|
||||
return budgetRepo.findAll()
|
||||
.flatMap { budget ->
|
||||
getCategoryTransactionPipeline(budget.dateFrom, budget.dateTo, "INCOME")
|
||||
.map { categories ->
|
||||
budget.incomeCategories = categories
|
||||
spaceService.getSpace("67af3c0f652da946a7dd9931")
|
||||
.map { space ->
|
||||
budget.space = space
|
||||
budget
|
||||
}
|
||||
.flatMap { updatedBudget -> budgetRepo.save(updatedBudget) }
|
||||
@@ -267,12 +304,38 @@ class FinancialService(
|
||||
.then()
|
||||
}
|
||||
|
||||
fun regenTransactions(): Mono<Void> {
|
||||
return transactionsRepo.findAll().flatMap { transaction ->
|
||||
spaceService.getSpace("67af3c0f652da946a7dd9931")
|
||||
.map { space ->
|
||||
transaction.space = space
|
||||
transaction
|
||||
}
|
||||
.flatMap { updatedTransaction -> transactionsRepo.save(updatedTransaction) }
|
||||
}
|
||||
.then()
|
||||
}
|
||||
|
||||
|
||||
fun regenCats(): Mono<Void> {
|
||||
return categoryRepo.findAll()// Получаем список категорий
|
||||
.flatMap { cat ->
|
||||
spaceService.getSpace("67af3c0f652da946a7dd9931") // Получаем space
|
||||
.map { space ->
|
||||
cat.space = space // Привязываем пространство к категории
|
||||
cat
|
||||
}
|
||||
}
|
||||
.flatMap { updatedCategory -> categoryRepo.save(updatedCategory) } // Сохраняем в БД
|
||||
.then() // Завершаем Mono<Void>
|
||||
}
|
||||
|
||||
@CacheEvict(cacheNames = ["budgets", "budgetsList"], allEntries = true)
|
||||
fun createBudget(budget: Budget, createRecurrent: Boolean): Mono<Budget> {
|
||||
fun createBudget(spaceId: String, budget: Budget, createRecurrent: Boolean): Mono<Budget> {
|
||||
return Mono.zip(
|
||||
getBudgetByDate(budget.dateFrom).map { Optional.ofNullable(it) }
|
||||
getBudgetByDate(budget.dateFrom, spaceId).map { Optional.ofNullable(it) }
|
||||
.switchIfEmpty(Mono.just(Optional.empty())),
|
||||
getBudgetByDate(budget.dateTo).map { Optional.ofNullable(it) }
|
||||
getBudgetByDate(budget.dateTo, spaceId).map { Optional.ofNullable(it) }
|
||||
.switchIfEmpty(Mono.just(Optional.empty()))
|
||||
).flatMap { tuple ->
|
||||
val startBudget = tuple.t1.orElse(null)
|
||||
@@ -283,36 +346,59 @@ class FinancialService(
|
||||
return@flatMap Mono.error<Budget>(IllegalArgumentException("Бюджет с теми же датами найден"))
|
||||
}
|
||||
|
||||
// Если createRecurrent=true, создаем рекуррентные транзакции
|
||||
val recurrentsCreation = if (createRecurrent) {
|
||||
recurrentService.createRecurrentsForBudget(budget)
|
||||
} else {
|
||||
Mono.empty()
|
||||
}
|
||||
// Получаем Space по spaceId
|
||||
return@flatMap spaceService.getSpace(spaceId)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space not found for $spaceId")))
|
||||
|
||||
// Создаем бюджет после возможного создания рекуррентных транзакций
|
||||
recurrentsCreation.then(
|
||||
getCategoryTransactionPipeline(budget.dateFrom, budget.dateTo)
|
||||
.flatMap { categories ->
|
||||
budget.categories = categories
|
||||
budgetRepo.save(budget)
|
||||
}
|
||||
.publishOn(reactor.core.scheduler.Schedulers.boundedElastic())
|
||||
.doOnNext { savedBudget ->
|
||||
// Выполнение updateBudgetWarns в фоне
|
||||
updateBudgetWarns(budget = savedBudget)
|
||||
.doOnError { error ->
|
||||
// Логируем ошибку, если произошла
|
||||
println("Error during updateBudgetWarns: ${error.message}")
|
||||
.flatMap { space ->
|
||||
// Проверяем, входит ли пользователь в этот Space
|
||||
ReactiveSecurityContextHolder.getContext().flatMap { securityContext ->
|
||||
val username = securityContext.authentication.name
|
||||
userService.getByUsername(username)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
||||
.flatMap { user ->
|
||||
if (space.users.none { it.id == user.id }) {
|
||||
return@flatMap Mono.error<Budget>(IllegalArgumentException("User does not have access to this space"))
|
||||
}
|
||||
|
||||
// Присваиваем Space бюджету
|
||||
budget.space = space
|
||||
|
||||
// Если createRecurrent=true, создаем рекуррентные транзакции
|
||||
val recurrentsCreation = if (createRecurrent) {
|
||||
recurrentService.createRecurrentsForBudget(space, budget)
|
||||
} else {
|
||||
Mono.empty()
|
||||
}
|
||||
|
||||
// Создаем бюджет после возможного создания рекуррентных транзакций
|
||||
recurrentsCreation.then(
|
||||
getCategoryTransactionPipeline(budget.dateFrom, budget.dateTo)
|
||||
.flatMap { categories ->
|
||||
budget.categories = categories
|
||||
budgetRepo.save(budget)
|
||||
}
|
||||
.publishOn(reactor.core.scheduler.Schedulers.boundedElastic())
|
||||
.doOnNext { savedBudget ->
|
||||
// Выполнение updateBudgetWarns в фоне
|
||||
updateBudgetWarns(budget = savedBudget)
|
||||
.doOnError { error ->
|
||||
// Логируем ошибку, если произошла
|
||||
println("Error during updateBudgetWarns: ${error.message}")
|
||||
}
|
||||
.subscribe()
|
||||
}
|
||||
)
|
||||
}
|
||||
.subscribe()
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getBudgetByDate(date: LocalDate): Mono<Budget> {
|
||||
return budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThanEqual(date, date).switchIfEmpty(Mono.empty())
|
||||
fun getBudgetByDate(date: LocalDate, spaceId: String): Mono<Budget> {
|
||||
return budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThanEqualAndSpace(date, date, ObjectId(spaceId))
|
||||
.switchIfEmpty(Mono.empty())
|
||||
}
|
||||
|
||||
|
||||
@@ -530,6 +616,7 @@ class FinancialService(
|
||||
|
||||
@Cacheable("transactions")
|
||||
fun getTransactions(
|
||||
spaceId: String,
|
||||
dateFrom: LocalDate? = null,
|
||||
dateTo: LocalDate? = null,
|
||||
transactionType: String? = null,
|
||||
@@ -543,46 +630,71 @@ class FinancialService(
|
||||
limit: Int? = null,
|
||||
offset: Int? = null,
|
||||
): Mono<MutableList<Transaction>> {
|
||||
val matchCriteria = mutableListOf<Criteria>()
|
||||
return ReactiveSecurityContextHolder.getContext()
|
||||
.map { it.authentication }
|
||||
.flatMap { authentication ->
|
||||
val username = authentication.name
|
||||
spaceService.getSpace(spaceId)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space not found for $spaceId")))
|
||||
.flatMap { space ->
|
||||
userService.getByUsername(username)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
||||
.flatMap { user ->
|
||||
if (space.users.none { it.id.toString() == user.id }) {
|
||||
return@flatMap Mono.error<MutableList<Transaction>>(IllegalArgumentException("User does not have access to this Space"))
|
||||
}
|
||||
|
||||
// Добавляем фильтры
|
||||
dateFrom?.let { matchCriteria.add(Criteria.where("date").gte(it)) }
|
||||
dateTo?.let { matchCriteria.add(Criteria.where("date").lt(it)) }
|
||||
transactionType?.let { matchCriteria.add(Criteria.where("type.code").`is`(it)) }
|
||||
isDone?.let { matchCriteria.add(Criteria.where("isDone").`is`(it)) }
|
||||
categoryId?.let { matchCriteria.add(Criteria.where("categoryDetails._id").`is`(it)) }
|
||||
categoryType?.let { matchCriteria.add(Criteria.where("categoryDetails.type.code").`is`(it)) }
|
||||
userId?.let { matchCriteria.add(Criteria.where("userDetails._id").`is`(ObjectId(it))) }
|
||||
parentId?.let { matchCriteria.add(Criteria.where("parentId").`is`(it)) }
|
||||
isChild?.let { matchCriteria.add(Criteria.where("parentId").exists(it)) }
|
||||
val matchCriteria = mutableListOf<Criteria>()
|
||||
|
||||
// Сборка агрегации
|
||||
val lookup = lookup("categories", "category.\$id", "_id", "categoryDetails")
|
||||
val lookupUsers = lookup("users", "user.\$id", "_id", "userDetails")
|
||||
val match = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
// Добавляем фильтры
|
||||
matchCriteria.add(Criteria.where("spaceDetails._id").`is`(ObjectId(spaceId)))
|
||||
dateFrom?.let { matchCriteria.add(Criteria.where("date").gte(it)) }
|
||||
dateTo?.let { matchCriteria.add(Criteria.where("date").lt(it)) }
|
||||
transactionType?.let { matchCriteria.add(Criteria.where("type.code").`is`(it)) }
|
||||
isDone?.let { matchCriteria.add(Criteria.where("isDone").`is`(it)) }
|
||||
categoryId?.let { matchCriteria.add(Criteria.where("categoryDetails._id").`is`(it)) }
|
||||
categoryType?.let {
|
||||
matchCriteria.add(
|
||||
Criteria.where("categoryDetails.type.code").`is`(it)
|
||||
)
|
||||
}
|
||||
userId?.let { matchCriteria.add(Criteria.where("userDetails._id").`is`(ObjectId(it))) }
|
||||
parentId?.let { matchCriteria.add(Criteria.where("parentId").`is`(it)) }
|
||||
isChild?.let { matchCriteria.add(Criteria.where("parentId").exists(it)) }
|
||||
|
||||
var sort = sort(Sort.by(Direction.DESC, "date").and(Sort.by(Direction.DESC, "createdAt")))
|
||||
// Сборка агрегации
|
||||
val lookup = lookup("categories", "category.\$id", "_id", "categoryDetails")
|
||||
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
||||
val lookupUsers = lookup("users", "user.\$id", "_id", "userDetails")
|
||||
val match = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
|
||||
sortSetting?.let {
|
||||
sort = sort(Sort.by(it.order, it.by).and(Sort.by(Direction.ASC, "createdAt")))
|
||||
}
|
||||
var sort =
|
||||
sort(Sort.by(Direction.DESC, "date").and(Sort.by(Direction.DESC, "createdAt")))
|
||||
|
||||
val aggregationBuilder = mutableListOf(
|
||||
lookup,
|
||||
lookupUsers,
|
||||
match.takeIf { matchCriteria.isNotEmpty() },
|
||||
sort,
|
||||
offset?.let { skip(it.toLong()) },
|
||||
limit?.let { limit(it.toLong()) }
|
||||
).filterNotNull()
|
||||
sortSetting?.let {
|
||||
sort = sort(Sort.by(it.order, it.by).and(Sort.by(Direction.ASC, "createdAt")))
|
||||
}
|
||||
|
||||
val aggregation = newAggregation(aggregationBuilder)
|
||||
val aggregationBuilder = mutableListOf(
|
||||
lookup,
|
||||
lookupSpaces,
|
||||
lookupUsers,
|
||||
match.takeIf { matchCriteria.isNotEmpty() },
|
||||
sort,
|
||||
offset?.let { skip(it.toLong()) },
|
||||
limit?.let { limit(it.toLong()) }
|
||||
).filterNotNull()
|
||||
|
||||
return reactiveMongoTemplate.aggregate(
|
||||
aggregation, "transactions", Transaction::class.java
|
||||
)
|
||||
.collectList() // Преобразуем Flux<Transaction> в Mono<List<Transaction>>
|
||||
.map { it.toMutableList() }
|
||||
val aggregation = newAggregation(aggregationBuilder)
|
||||
|
||||
return@flatMap reactiveMongoTemplate.aggregate(
|
||||
aggregation, "transactions", Transaction::class.java
|
||||
)
|
||||
.collectList()
|
||||
.map { it.toMutableList() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getTransactionsToDelete(dateFrom: LocalDate, dateTo: LocalDate): Mono<List<Transaction>> {
|
||||
@@ -618,19 +730,29 @@ class FinancialService(
|
||||
|
||||
|
||||
@CacheEvict(cacheNames = ["transactions"], allEntries = true)
|
||||
fun createTransaction(transaction: Transaction): Mono<Transaction> {
|
||||
fun createTransaction(spaceId: String, transaction: Transaction): Mono<Transaction> {
|
||||
return ReactiveSecurityContextHolder.getContext()
|
||||
.map { it.authentication }
|
||||
.flatMap { authentication ->
|
||||
val username = authentication.name
|
||||
userService.getByUsername(username)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
||||
.flatMap { user ->
|
||||
transaction.user = user
|
||||
transactionsRepo.save(transaction)
|
||||
.flatMap { savedTransaction ->
|
||||
updateBudgetOnCreate(savedTransaction)
|
||||
.thenReturn(savedTransaction) // Ждём выполнения updateBudgetOnCreate перед возвратом
|
||||
spaceService.getSpace(spaceId)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space not found for $spaceId")))
|
||||
.flatMap { space ->
|
||||
userService.getByUsername(username)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
||||
.flatMap { user ->
|
||||
if (space.users.none { it.id.toString() == user.id }) {
|
||||
return@flatMap Mono.error<Transaction>(IllegalArgumentException("User does not have access to this Space"))
|
||||
}
|
||||
// Привязываем space и user к транзакции
|
||||
transaction.user = user
|
||||
transaction.space = space
|
||||
|
||||
transactionsRepo.save(transaction)
|
||||
.flatMap { savedTransaction ->
|
||||
updateBudgetOnCreate(savedTransaction)
|
||||
.thenReturn(savedTransaction) // Ждём выполнения updateBudgetOnCreate перед возвратом
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1034,8 +1156,8 @@ class FinancialService(
|
||||
tgUserName = userDocument["tgUserName"]?.let { it as String },
|
||||
password = null,
|
||||
isActive = userDocument["isActive"] as Boolean,
|
||||
regDate = userDocument["regDate"] as Date,
|
||||
createdAt = userDocument["createdAt"] as Date,
|
||||
regDate = userDocument["regDate"] as LocalDate,
|
||||
createdAt = userDocument["createdAt"] as LocalDateTime,
|
||||
roles = userDocument["roles"] as ArrayList<String>,
|
||||
)
|
||||
|
||||
@@ -1051,6 +1173,7 @@ class FinancialService(
|
||||
)
|
||||
return Transaction(
|
||||
(document["_id"] as ObjectId).toString(),
|
||||
null,
|
||||
TransactionType(
|
||||
transactionType["code"] as String,
|
||||
transactionType["name"] as String
|
||||
@@ -1582,7 +1705,7 @@ class FinancialService(
|
||||
.collectList()
|
||||
}
|
||||
|
||||
fun getCategorySummaries(dateFrom: LocalDate): Mono<List<Document>> {
|
||||
fun getCategorySummaries(spaceId: String, dateFrom: LocalDate): Mono<List<Document>> {
|
||||
val sixMonthsAgo = Date.from(
|
||||
LocalDateTime.of(dateFrom, LocalTime.MIN)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
@@ -1591,6 +1714,16 @@ class FinancialService(
|
||||
|
||||
val aggregation = listOf(
|
||||
// 1. Фильтр за последние 6 месяцев
|
||||
Document(
|
||||
"\$lookup", Document("from", "spaces")
|
||||
.append("localField", "space.\$id")
|
||||
.append("foreignField", "_id")
|
||||
.append("as", "spaceInfo")
|
||||
),
|
||||
|
||||
// 4. Распаковываем массив категорий
|
||||
Document("\$unwind", "\$spaceInfo"),
|
||||
Document("\$match", Document("spaceInfo._id", ObjectId(spaceId))),
|
||||
Document(
|
||||
"\$match",
|
||||
Document("date", Document("\$gte", sixMonthsAgo).append("\$lt", Date())).append("type.code", "INSTANT")
|
||||
@@ -1720,6 +1853,7 @@ class FinancialService(
|
||||
Document("\$sort", Document("categoryName", 1))
|
||||
)
|
||||
|
||||
|
||||
// Выполняем агрегацию
|
||||
return reactiveMongoTemplate.getCollection("transactions")
|
||||
.flatMapMany { it.aggregate(aggregation) }
|
||||
|
||||
Reference in New Issue
Block a user