diff --git a/src/main/kotlin/space/luminic/budgerapp/controllers/SpaceController.kt b/src/main/kotlin/space/luminic/budgerapp/controllers/SpaceController.kt index 29d5cea..d39ed33 100644 --- a/src/main/kotlin/space/luminic/budgerapp/controllers/SpaceController.kt +++ b/src/main/kotlin/space/luminic/budgerapp/controllers/SpaceController.kt @@ -5,8 +5,10 @@ import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import org.springframework.web.client.HttpClientErrorException +import reactor.core.publisher.Flux import reactor.core.publisher.Mono import space.luminic.budgerapp.controllers.BudgetController.LimitValue +import space.luminic.budgerapp.controllers.dtos.BudgetCreationDTO import space.luminic.budgerapp.models.* import space.luminic.budgerapp.services.CategoryService import space.luminic.budgerapp.services.FinancialService @@ -101,10 +103,10 @@ class SpaceController( @PostMapping("/{spaceId}/budgets") fun createBudget( @PathVariable spaceId: String, - @RequestBody budget: Budget + @RequestBody budgetCreationDTO: BudgetCreationDTO, ): Mono { return spaceService.isValidRequest(spaceId).flatMap { - financialService.createBudget(it, budget) + financialService.createBudget(it, budgetCreationDTO.budget, budgetCreationDTO.createRecurrent) } } @@ -302,7 +304,7 @@ class SpaceController( } } - @PostMapping("/{spaceId}/recurrents") + @PostMapping("/{spaceId}/recurrent") fun createRecurrent(@PathVariable spaceId: String, @RequestBody recurrent: Recurrent): Mono { return spaceService.isValidRequest(spaceId).flatMap { recurrentService.createRecurrent(it, recurrent) @@ -310,7 +312,7 @@ class SpaceController( } - @PutMapping("/{spaceId}/recurrents/{id}") + @PutMapping("/{spaceId}/recurrent/{id}") fun editRecurrent( @PathVariable spaceId: String, @PathVariable id: String, @@ -330,8 +332,8 @@ class SpaceController( } } - @GetMapping("/regen") - fun regenSpaces(): Mono { - return spaceService.regenSpaceCategory() - } +// @GetMapping("/regen") +// fun regenSpaces(): Mono { +// return spaceService.regenSpaceCategory() +// } } \ No newline at end of file diff --git a/src/main/kotlin/space/luminic/budgerapp/services/FinancialService.kt b/src/main/kotlin/space/luminic/budgerapp/services/FinancialService.kt index 4694c3d..4bbc6a1 100644 --- a/src/main/kotlin/space/luminic/budgerapp/services/FinancialService.kt +++ b/src/main/kotlin/space/luminic/budgerapp/services/FinancialService.kt @@ -1,5 +1,6 @@ package space.luminic.budgerapp.services +import com.mongodb.DBRef import org.bson.BsonNull import org.bson.Document import org.bson.types.ObjectId @@ -11,7 +12,9 @@ import org.springframework.data.domain.Sort.Direction import org.springframework.data.mongodb.core.ReactiveMongoTemplate import org.springframework.data.mongodb.core.aggregation.Aggregation.* import org.springframework.data.mongodb.core.aggregation.DateOperators.DateToString +import org.springframework.data.mongodb.core.aggregation.SortOperation import org.springframework.data.mongodb.core.query.Criteria +import org.springframework.data.mongodb.core.query.Query import org.springframework.security.core.context.ReactiveSecurityContextHolder import org.springframework.stereotype.Service import reactor.core.publisher.Flux @@ -33,6 +36,7 @@ class FinancialService( val budgetRepo: BudgetRepo, val warnRepo: WarnRepo, val transactionsRepo: TransactionRepo, + val recurrentService: RecurrentService, val userService: UserService, val reactiveMongoTemplate: ReactiveMongoTemplate, private val categoryRepo: CategoryRepo, @@ -207,6 +211,7 @@ class FinancialService( val unwindSpace = unwind("spaceDetails") val matchStage = match(Criteria.where("spaceDetails._id").`is`(spaceId)) // matchCriteria.add(Criteria.where("spaceDetails._id").`is`(ObjectId(spaceId))) + val projectStage = project("_id", "name", "dateFrom", "dateTo") // Оставляем только нужные поля val sort = sortRequested?.let { sort(it) } ?: sort( Sort.by(Direction.DESC, "date").and(Sort.by(Direction.DESC, "createdAt")) ) @@ -224,9 +229,11 @@ class FinancialService( spaceId: String, budgetId: String? = null, dateFrom: LocalDate? = null, dateTo: LocalDate? = null ): Mono { val lookupCategories = lookup("categories", "categories.category.\$id", "_id", "categoriesDetails") + val unwindCategories = unwind("categoriesDetails") val lookupIncomeCategories = lookup("categories", "incomeCategories.category.\$id", "_id", "incomeCategoriesDetails") + val unwindIncomeCategories = unwind("incomeCategoriesDetails") val lookupSpace = lookup("spaces", "space.\$id", "_id", "spaceDetails") val unwindSpace = unwind("spaceDetails") val matchCriteria = mutableListOf() @@ -347,7 +354,7 @@ class FinancialService( } - fun createBudget(space: Space, budget: Budget): Mono { + fun createBudget(space: Space, budget: Budget, createRecurrent: Boolean): Mono { return Mono.zip(getBudgetByDate(budget.dateFrom, space.id!!).map { Optional.ofNullable(it) } .switchIfEmpty(Mono.just(Optional.empty())), getBudgetByDate(budget.dateTo, space.id!!).map { Optional.ofNullable(it) } @@ -360,22 +367,35 @@ class FinancialService( return@flatMap Mono.error(IllegalArgumentException("Бюджет с теми же датами найден")) } + // Получаем Space по spaceId + + + // Присваиваем Space бюджету budget.space = space - getCategoryTransactionPipeline( - space.id!!, - budget.dateFrom, - budget.dateTo - ).flatMap { categories -> - budget.categories = categories - budgetRepo.save(budget) - }.publishOn(Schedulers.boundedElastic()).doOnNext { savedBudget -> - // Выполнение updateBudgetWarns в фоне - updateBudgetWarns(budget = savedBudget).doOnError { error -> - // Логируем ошибку, если произошла - logger.error("Error during updateBudgetWarns: ${error.message}") - }.subscribe() - }.then( + // Если createRecurrent=true, создаем рекуррентные транзакции + val recurrentsCreation = if (createRecurrent) { + recurrentService.createRecurrentsForBudget(space, budget) + } else { + Mono.empty() + } + + // Создаем бюджет после возможного создания рекуррентных транзакций + recurrentsCreation.then( + getCategoryTransactionPipeline( + space.id!!, + budget.dateFrom, + budget.dateTo + ).flatMap { categories -> + budget.categories = categories + budgetRepo.save(budget) + }.publishOn(Schedulers.boundedElastic()).doOnNext { savedBudget -> + // Выполнение updateBudgetWarns в фоне + updateBudgetWarns(budget = savedBudget).doOnError { error -> + // Логируем ошибку, если произошла + logger.error("Error during updateBudgetWarns: ${error.message}") + }.subscribe() + }).then( getCategoryTransactionPipeline( space.id!!, budget.dateFrom, @@ -616,7 +636,7 @@ class FinancialService( 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`(ObjectId(it))) } + categoryId?.let { matchCriteria.add(Criteria.where("categoryDetails._id").`is`(it)) } categoryType?.let { matchCriteria.add( Criteria.where("categoryDetails.type.code").`is`(it) @@ -1124,7 +1144,7 @@ class FinancialService( private fun documentToTransactionMapper(document: Document): Transaction { val transactionType = document["type"] as Document - val user: User? + var user: User? val userDocument = document["userDetailed"] as Document user = User( @@ -1325,7 +1345,7 @@ class FinancialService( fun getCategoryTransactionPipeline( - spaceId: String, dateFrom: LocalDate, dateTo: LocalDate, categoryType: String? = "EXPENSE" + spaceId: String, dateFrom: LocalDate, dateTo: LocalDate, catType: String? = "EXPENSE" ): Mono> { val pipeline = listOf( Document( @@ -1338,7 +1358,7 @@ class FinancialService( "\$match", Document( Document("spaceDetailed._id", ObjectId(spaceId)) ) - ), Document("\$match", Document("type.code", categoryType)), Document( + ), Document("\$match", Document("type.code", catType)), Document( "\$lookup", Document("from", "transactions").append( "let", Document("categoryId", "\$_id") ).append( @@ -1594,7 +1614,7 @@ class FinancialService( Document("\$unwind", "\$categoryInfo"), // 5. Фильтруем по типу категории (EXPENSE) -// Document("\$match", Document("categoryInfo.type.code", "EXPENSE")), + Document("\$match", Document("categoryInfo.type.code", "EXPENSE")), // 6. Группируем обратно по категории, собирая все (год, месяц, total) Document(