+ targets

This commit is contained in:
xds
2025-11-20 12:37:06 +03:00
parent 195bdd83f0
commit 2452e5935f
17 changed files with 361 additions and 290 deletions

View File

@@ -1,19 +0,0 @@
package space.luminic.finance.api
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import space.luminic.finance.dtos.GoalDTO
import space.luminic.finance.mappers.GoalMapper.toDto
import space.luminic.finance.services.GoalService
@RestController
@RequestMapping("/spaces/{spaceId}/goals")
class GoalController(private val goalService: GoalService) {
@GetMapping
fun findAll(@PathVariable spaceId: Int): List<GoalDTO> {
return goalService.findAllBySpaceId(spaceId).map { it.toDto() }
}
}

View File

@@ -0,0 +1,19 @@
package space.luminic.finance.api
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import space.luminic.finance.dtos.TargetDTO
import space.luminic.finance.mappers.TargetMapper.toDto
import space.luminic.finance.services.TargetService
@RestController
@RequestMapping("/spaces/{spaceId}/targets")
class TargetController(private val targetService: TargetService) {
@GetMapping
fun findAll(@PathVariable spaceId: Int): List<TargetDTO> {
return targetService.findAllBySpaceId(spaceId).map { it.toDto() }
}
}

View File

@@ -1,36 +1,37 @@
package space.luminic.finance.dtos package space.luminic.finance.dtos
import space.luminic.finance.models.Goal import space.luminic.finance.models.Target
import space.luminic.finance.models.Goal.GoalType import space.luminic.finance.models.Target.TargetType
import space.luminic.finance.models.Transaction import space.luminic.finance.models.Transaction
import java.math.BigDecimal import java.math.BigDecimal
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
data class GoalDTO( data class TargetDTO(
val id: Int, val id: Int,
val type: GoalType, val type: TargetType,
val name: String, val name: String,
val description: String? = null, val description: String? = null,
val amount: BigDecimal, val amount: BigDecimal,
val currentAmount: BigDecimal,
val date: LocalDate, val date: LocalDate,
val components: List<Goal.GoalComponent>, val components: List<Target.TargetComponent>,
val transactions: List<Transaction>, val transactions: List<Transaction>,
val createdBy: UserDTO, val createdBy: UserDTO,
val createdAt: Instant, val createdAt: Instant,
val updatedBy: UserDTO? = null, val updatedBy: UserDTO? = null,
val updatedAt: Instant? = null, val updatedAt: Instant? = null,
) { ) {
data class CreateGoalDTO( data class CreateTargetDTO(
val type: GoalType, val type: TargetType,
val name: String, val name: String,
val description: String?, val description: String?,
val amount: BigDecimal, val amount: BigDecimal,
val date: LocalDate val date: LocalDate
) )
data class UpdateGoalDTO( data class UpdateTargetDTO(
val type: GoalType, val type: TargetType,
val name: String, val name: String,
val description: String?, val description: String?,
val amount: BigDecimal, val amount: BigDecimal,

View File

@@ -1,22 +1,26 @@
package space.luminic.finance.mappers package space.luminic.finance.mappers
import space.luminic.finance.dtos.GoalDTO import space.luminic.finance.dtos.TargetDTO
import space.luminic.finance.mappers.UserMapper.toDto import space.luminic.finance.mappers.UserMapper.toDto
import space.luminic.finance.models.Goal import space.luminic.finance.models.Target
object GoalMapper { object TargetMapper {
fun Goal.toDto() = GoalDTO( fun Target.toDto() = TargetDTO(
id = this.id ?: throw IllegalArgumentException("Goal id is not provided"), id = this.id ?: throw IllegalArgumentException("Target id is not provided"),
type = this.type, type = this.type,
name = this.name, name = this.name,
description = this.description,
amount = this.amount, amount = this.amount,
currentAmount = this.currentAmount,
date = this.untilDate, date = this.untilDate,
components = this.components, components = this.components,
transactions = this.transactions, transactions = this.transactions,
createdBy = (this.createdBy ?: throw IllegalArgumentException("created by not provided")).toDto(), createdBy = (this.createdBy ?: throw IllegalArgumentException("created by not provided")).toDto(),
createdAt = this.createdAt ?: throw IllegalArgumentException("created at not provided"), createdAt = this.createdAt ?: throw IllegalArgumentException("created at not provided"),
updatedBy = this.updatedBy?.toDto() , updatedBy = this.updatedBy?.toDto(),
updatedAt = this.updatedAt updatedAt = this.updatedAt,
)
)
} }

View File

@@ -8,30 +8,30 @@ import java.math.BigDecimal
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
data class Goal( data class Target(
var id: Int? = null, var id: Int? = null,
val space: Space? = null, val space: Space? = null,
val type: GoalType, val type: TargetType,
val name: String, val name: String,
val description: String? = null, val description: String? = null,
val amount: BigDecimal, val amount: BigDecimal,
val components: List<GoalComponent> = emptyList(), val components: List<TargetComponent> = emptyList(),
val transactions: List<Transaction> = emptyList(), val transactions: List<Transaction> = emptyList(),
val untilDate: LocalDate, val untilDate: LocalDate,
@CreatedBy var createdBy: User? = null, var createdBy: User? = null,
@CreatedDate var createdAt: Instant? = null, var createdAt: Instant? = null,
@LastModifiedBy var updatedBy: User? = null, var updatedBy: User? = null,
@LastModifiedDate var updatedAt: Instant? = null, var updatedAt: Instant? = null,
) { ) {
var currentAmount: BigDecimal = { var currentAmount: BigDecimal = {
this.transactions.sumOf { it.amount } this.transactions.sumOf { it.amount }
} as BigDecimal } as BigDecimal
data class GoalComponent( data class TargetComponent(
val id: Int? = null, val id: Int? = null,
val name: String, val name: String,
val amount: BigDecimal, val amount: BigDecimal,
@@ -39,8 +39,9 @@ data class Goal(
val date: LocalDate = LocalDate.now(), val date: LocalDate = LocalDate.now(),
) )
enum class GoalType(val displayName: String, val icon: String) { enum class TargetType(val displayName: String, val icon: String) {
AUTO("Авто", "🏎️"), AUTO("Авто", "🏎️"),
LEISURE("Досуг", "💃"),
VACATION("Отпуск", "🏖️"), VACATION("Отпуск", "🏖️"),
GOODS("Покупка", "🛍️"), GOODS("Покупка", "🛍️"),
OTHER("Прочее", "💸") OTHER("Прочее", "💸")

View File

@@ -1,20 +0,0 @@
package space.luminic.finance.repos
import space.luminic.finance.models.Goal
interface GoalRepo {
fun findAllBySpaceId(spaceId: Int) : List<Goal>
fun findBySpaceIdAndId(spaceId: Int, id: Int) : Goal?
fun create(goal: Goal, createdById: Int): Int
fun update(goal: Goal, updatedById: Int)
fun delete(spaceId: Int, id: Int)
fun getComponents(spaceId: Int, goalId: Int): List<Goal.GoalComponent>
fun getComponent(spaceId: Int, goalId: Int, id: Int): Goal.GoalComponent?
fun createComponent(goalId: Int, component: Goal.GoalComponent, createdById: Int): Int
fun updateComponent(goalId: Int, componentId: Int, component: Goal.GoalComponent, updatedById: Int)
fun deleteComponent(goalId: Int, componentId: Int)
fun assignTransaction(goalId: Int, transactionId: Int)
fun refuseTransaction(goalId: Int, transactionId: Int)
}

View File

@@ -0,0 +1,20 @@
package space.luminic.finance.repos
import space.luminic.finance.models.Target
interface TargetRepo {
fun findAllBySpaceId(spaceId: Int) : List<Target>
fun findBySpaceIdAndId(spaceId: Int, id: Int) : Target?
fun create(target: Target, createdById: Int): Int
fun update(target: Target, updatedById: Int)
fun delete(spaceId: Int, id: Int)
fun getComponents(spaceId: Int, targetId: Int): List<Target.TargetComponent>
fun getComponent(spaceId: Int, targetId: Int, id: Int): Target.TargetComponent?
fun createComponent(targetId: Int, component: Target.TargetComponent, createdById: Int): Int
fun updateComponent(targetId: Int, componentId: Int, component: Target.TargetComponent, updatedById: Int)
fun deleteComponent(targetId: Int, componentId: Int)
fun assignTransaction(targetId: Int, transactionId: Int)
fun refuseTransaction(targetId: Int, transactionId: Int)
}

View File

@@ -3,17 +3,17 @@ package space.luminic.finance.repos
import org.springframework.jdbc.core.RowMapper import org.springframework.jdbc.core.RowMapper
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
import space.luminic.finance.models.Goal import space.luminic.finance.models.Target
import space.luminic.finance.models.User import space.luminic.finance.models.User
@Repository @Repository
class GoalRepoImpl( class TargetRepoImpl(
private val jdbcTemplate: NamedParameterJdbcTemplate private val jdbcTemplate: NamedParameterJdbcTemplate
) : GoalRepo { ) : TargetRepo {
private val goalRowMapper = RowMapper { rs, _ -> private val targetRowMapper = RowMapper { rs, _ ->
Goal( Target(
id = rs.getInt("g_id"), id = rs.getInt("g_id"),
type = Goal.GoalType.valueOf(rs.getString("g_type")), type = Target.TargetType.valueOf(rs.getString("g_type")),
name = rs.getString("g_name"), name = rs.getString("g_name"),
description = rs.getString("g_description"), description = rs.getString("g_description"),
amount = rs.getBigDecimal("g_amount"), amount = rs.getBigDecimal("g_amount"),
@@ -30,7 +30,7 @@ class GoalRepoImpl(
} }
private val componentRowMapper = RowMapper { rs, _ -> private val componentRowMapper = RowMapper { rs, _ ->
Goal.GoalComponent( Target.TargetComponent(
id = rs.getInt("gc_id"), id = rs.getInt("gc_id"),
name = rs.getString("gc_name"), name = rs.getString("gc_name"),
amount = rs.getBigDecimal("gc_amount"), amount = rs.getBigDecimal("gc_amount"),
@@ -39,7 +39,7 @@ class GoalRepoImpl(
) )
} }
override fun findAllBySpaceId(spaceId: Int): List<Goal> { override fun findAllBySpaceId(spaceId: Int): List<Target> {
val sql = """ val sql = """
select select
g.id as g_id, g.id as g_id,
@@ -51,7 +51,7 @@ class GoalRepoImpl(
created_by.username as created_by_username, created_by.username as created_by_username,
created_by.first_name as created_by_first_name, created_by.first_name as created_by_first_name,
g.created_at as g_created_at g.created_at as g_created_at
from finance.goals g from finance.targets g
join finance.users created_by on g.created_by_id = created_by.id join finance.users created_by on g.created_by_id = created_by.id
where g.space_id = :spaceId where g.space_id = :spaceId
@@ -60,10 +60,10 @@ class GoalRepoImpl(
val params = mapOf( val params = mapOf(
"space_id" to spaceId, "space_id" to spaceId,
) )
return jdbcTemplate.query(sql, params, goalRowMapper) return jdbcTemplate.query(sql, params, targetRowMapper)
} }
override fun findBySpaceIdAndId(spaceId: Int, id: Int): Goal? { override fun findBySpaceIdAndId(spaceId: Int, id: Int): Target? {
val sql = """ val sql = """
select select
g.id as g_id, g.id as g_id,
@@ -75,7 +75,7 @@ class GoalRepoImpl(
created_by.username as created_by_username, created_by.username as created_by_username,
created_by.first_name as created_by_first_name, created_by.first_name as created_by_first_name,
g.created_at as g_created_at g.created_at as g_created_at
from finance.goals g from finance.targets g
join finance.users created_by on g.created_by_id = created_by.id join finance.users created_by on g.created_by_id = created_by.id
where g.space_id = :spaceId and g.id = :id where g.space_id = :spaceId and g.id = :id
@@ -85,12 +85,12 @@ class GoalRepoImpl(
"space_id" to spaceId, "space_id" to spaceId,
"id" to id, "id" to id,
) )
return jdbcTemplate.query(sql, params, goalRowMapper).firstOrNull() return jdbcTemplate.query(sql, params, targetRowMapper).firstOrNull()
} }
override fun create(goal: Goal, createdById: Int): Int { override fun create(target: Target, createdById: Int): Int {
val sql = """ val sql = """
insert into finance.goals( insert into finance.targets(
type, type,
name, name,
description, description,
@@ -108,19 +108,19 @@ class GoalRepoImpl(
returning id returning id
""".trimIndent() """.trimIndent()
val params = mapOf( val params = mapOf(
"type" to goal.type, "type" to target.type,
"name" to goal.name, "name" to target.name,
"description" to goal.description, "description" to target.description,
"amount" to goal.amount, "amount" to target.amount,
"until_date" to goal.untilDate, "until_date" to target.untilDate,
"created_by_id" to createdById "created_by_id" to createdById
) )
return jdbcTemplate.queryForObject(sql, params, Int::class.java)!! return jdbcTemplate.queryForObject(sql, params, Int::class.java)!!
} }
override fun update(goal: Goal, updatedById: Int) { override fun update(target: Target, updatedById: Int) {
val sql = """ val sql = """
update finance.goals set update finance.targets set
type = :type, type = :type,
name = :name, name = :name,
description = :description, description = :description,
@@ -131,12 +131,12 @@ class GoalRepoImpl(
where id = :id where id = :id
""".trimIndent() """.trimIndent()
val params = mapOf( val params = mapOf(
"id" to goal.id, "id" to target.id,
"type" to goal.type.name, "type" to target.type.name,
"name" to goal.name, "name" to target.name,
"description" to goal.description, "description" to target.description,
"amount" to goal.amount, "amount" to target.amount,
"until_date" to goal.untilDate, "until_date" to target.untilDate,
"updated_by_id" to updatedById "updated_by_id" to updatedById
) )
@@ -145,7 +145,7 @@ class GoalRepoImpl(
override fun delete(spaceId: Int, id: Int) { override fun delete(spaceId: Int, id: Int) {
val sql = """ val sql = """
delete from finance.goals where id = :id delete from finance.targets where id = :id
""".trimIndent() """.trimIndent()
val params = mapOf( val params = mapOf(
@@ -156,8 +156,8 @@ class GoalRepoImpl(
override fun getComponents( override fun getComponents(
spaceId: Int, spaceId: Int,
goalId: Int targetId: Int
): List<Goal.GoalComponent> { ): List<Target.TargetComponent> {
val sql = """ val sql = """
select select
gc.id as gc_id, gc.id as gc_id,
@@ -165,11 +165,11 @@ class GoalRepoImpl(
gc.amount as gc_amount, gc.amount as gc_amount,
gc.is_done as gc_is_done, gc.is_done as gc_is_done,
gc.date as gc_date gc.date as gc_date
from finance.goals_components gc from finance.targets_components gc
where gc.goal_id = :goal_id where gc.target_id = :target_id
""".trimIndent() """.trimIndent()
val params = mapOf( val params = mapOf(
"goal_id" to goalId "target_id" to targetId
) )
return jdbcTemplate.query(sql, params, componentRowMapper) return jdbcTemplate.query(sql, params, componentRowMapper)
@@ -177,9 +177,9 @@ class GoalRepoImpl(
override fun getComponent( override fun getComponent(
spaceId: Int, spaceId: Int,
goalId: Int, targetId: Int,
id: Int id: Int
): Goal.GoalComponent? { ): Target.TargetComponent? {
val sql = """ val sql = """
select select
gc.id as gc_id, gc.id as gc_id,
@@ -187,27 +187,27 @@ class GoalRepoImpl(
gc.amount as gc_amount, gc.amount as gc_amount,
gc.is_done as gc_is_done, gc.is_done as gc_is_done,
gc.date as gc_date gc.date as gc_date
from finance.goals_components gc from finance.targets_components gc
where gc.goal_id = :goal_id and gc.id = :id where gc.target_id = :target_id and gc.id = :id
""".trimIndent() """.trimIndent()
val params = mapOf( val params = mapOf(
"goal_id" to goalId, "target_id" to targetId,
"id" to id "id" to id
) )
return jdbcTemplate.query(sql, params, componentRowMapper).firstOrNull() return jdbcTemplate.query(sql, params, componentRowMapper).firstOrNull()
} }
override fun createComponent(goalId: Int, component: Goal.GoalComponent, createdById: Int): Int { override fun createComponent(targetId: Int, component: Target.TargetComponent, createdById: Int): Int {
val sql = """ val sql = """
insert into finance.goals_components( insert into finance.targets_components(
goal_id, target_id,
name, name,
amount, amount,
is_done, is_done,
date, date,
created_by_id created_by_id
) values ( ) values (
:goal_id, :target_id,
:name, :name,
:amount, :amount,
:is_done, :is_done,
@@ -216,7 +216,7 @@ class GoalRepoImpl(
returning id returning id
""".trimIndent() """.trimIndent()
val params = mapOf( val params = mapOf(
"goal_id" to goalId, "target_id" to targetId,
"name" to component.name, "name" to component.name,
"amount" to component.amount, "amount" to component.amount,
"is_done" to component.isDone, "is_done" to component.isDone,
@@ -226,17 +226,17 @@ class GoalRepoImpl(
return jdbcTemplate.queryForObject(sql, params, Int::class.java)!! return jdbcTemplate.queryForObject(sql, params, Int::class.java)!!
} }
override fun updateComponent(goalId: Int, componentId: Int, component: Goal.GoalComponent, updatedById: Int) { override fun updateComponent(targetId: Int, componentId: Int, component: Target.TargetComponent, updatedById: Int) {
val sql = """ val sql = """
update finance.goals_components set update finance.targets_components set
name = :name, name = :name,
amount = :amount, amount = :amount,
is_done = :is_done, is_done = :is_done,
updated_by_id = :updated_by_id updated_by_id = :updated_by_id
where goal_id = :goalId and id = :componentId where target_id = :targetId and id = :componentId
""".trimIndent() """.trimIndent()
val params = mapOf( val params = mapOf(
"goalId" to goalId, "targetId" to targetId,
"componentId" to componentId, "componentId" to componentId,
"name" to component.name, "name" to component.name,
"amount" to component.amount, "amount" to component.amount,
@@ -247,35 +247,35 @@ class GoalRepoImpl(
jdbcTemplate.update(sql, params) jdbcTemplate.update(sql, params)
} }
override fun deleteComponent(goalId: Int, componentId: Int) { override fun deleteComponent(targetId: Int, componentId: Int) {
val sql = """ val sql = """
delete from finance.goals_components where goal_id = :goalId and id = :componentId delete from finance.targets_components where target_id = :targetId and id = :componentId
""".trimIndent() """.trimIndent()
val params = mapOf( val params = mapOf(
"goalId" to goalId, "targetId" to targetId,
"componentId" to componentId "componentId" to componentId
) )
jdbcTemplate.update(sql, params) jdbcTemplate.update(sql, params)
} }
override fun assignTransaction(goalId: Int, transactionId: Int) { override fun assignTransaction(targetId: Int, transactionId: Int) {
val sql = """ val sql = """
insert into finance.goals_transactions(goal_id, transactions_id) insert into finance.targets_transactions(target_id, transactions_id)
values (:goal_id, :transaction_id) values (:targetId, :transaction_id)
""".trimIndent() """.trimIndent()
val params = mapOf( val params = mapOf(
"goal_id" to goalId, "targetId" to targetId,
"transaction_id" to transactionId "transaction_id" to transactionId
) )
jdbcTemplate.update(sql, params) jdbcTemplate.update(sql, params)
} }
override fun refuseTransaction(goalId: Int, transactionId: Int) { override fun refuseTransaction(targetId: Int, transactionId: Int) {
val sql = """ val sql = """
delete from finance.goals_transactions where goal_id = :goalId and transactions_id = :transactionId delete from finance.targets_transactions where target_id = :goalId and transactions_id = :transactionId
""".trimIndent() """.trimIndent()
val params = mapOf( val params = mapOf(
"goal_id" to goalId, "target_id" to targetId,
"transaction_id" to transactionId "transaction_id" to transactionId
) )
jdbcTemplate.update(sql, params) jdbcTemplate.update(sql, params)

View File

@@ -7,6 +7,8 @@ import space.luminic.finance.models.Category
import space.luminic.finance.models.Transaction import space.luminic.finance.models.Transaction
import space.luminic.finance.models.User import space.luminic.finance.models.User
import space.luminic.finance.services.TransactionService import space.luminic.finance.services.TransactionService
import java.time.LocalDate
import java.time.LocalDateTime
@Repository @Repository
class TransactionRepoImpl( class TransactionRepoImpl(
@@ -109,6 +111,9 @@ class TransactionRepoImpl(
filters.dateFrom?.let { filters.dateFrom?.let {
sql += " AND t.date >= :dateFrom" sql += " AND t.date >= :dateFrom"
params.put("dateFrom", it) params.put("dateFrom", it)
} ?: {
sql += " AND t.date >= :dateFrom"
params.put("dateFrom", LocalDate.now().minusMonths(1))
} }
filters.dateTo?.let { filters.dateTo?.let {
sql += " AND t.date <= :dateTo" sql += " AND t.date <= :dateTo"

View File

@@ -1,22 +0,0 @@
package space.luminic.finance.services
import space.luminic.finance.dtos.GoalDTO
import space.luminic.finance.models.Goal
interface GoalService {
fun findAllBySpaceId(spaceId: Int): List<Goal>
fun findBySpaceIdAndId(spaceId: Int, id: Int): Goal
fun create(spaceId: Int,goal: GoalDTO.CreateGoalDTO): Int
fun update(spaceId: Int, goalId: Int, goal: GoalDTO.UpdateGoalDTO)
fun delete(spaceId: Int, id: Int)
fun getComponents(spaceId: Int, goalId: Int): List<Goal.GoalComponent>
fun getComponent(spaceId: Int, goalId: Int, id: Int): Goal.GoalComponent?
fun createComponent(spaceId: Int, goalId: Int, component: Goal.GoalComponent): Int
fun updateComponent(spaceId: Int, goalId: Int, component: Goal.GoalComponent)
fun deleteComponent(spaceId: Int, goalId: Int, id: Int)
fun assignTransaction(spaceId: Int, goalId: Int, transactionId: Int)
fun refuseTransaction(spaceId: Int,goalId: Int, transactionId: Int)
}

View File

@@ -1,140 +0,0 @@
package space.luminic.finance.services
import org.springframework.stereotype.Service
import space.luminic.finance.dtos.GoalDTO
import space.luminic.finance.models.Goal
import space.luminic.finance.models.NotFoundException
import space.luminic.finance.repos.GoalRepo
import space.luminic.finance.repos.SpaceRepo
import space.luminic.finance.repos.TransactionRepo
@Service
class GoalServiceImpl(
private val goalRepo: GoalRepo,
private val spaceRepo: SpaceRepo,
private val authService: AuthService,
private val transactionRepo: TransactionRepo
) : GoalService {
override fun findAllBySpaceId(spaceId: Int): List<Goal> {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
return goalRepo.findAllBySpaceId(spaceId)
}
override fun findBySpaceIdAndId(spaceId: Int, id: Int): Goal {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
return goalRepo.findBySpaceIdAndId(spaceId, userId) ?: throw NotFoundException("Goal $id not found")
}
override fun create(spaceId: Int, goal: GoalDTO.CreateGoalDTO): Int {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
val creatingGoal = Goal(
type = goal.type,
name = goal.name,
amount = goal.amount,
untilDate = goal.date
)
return goalRepo.create(creatingGoal, userId)
}
override fun update(spaceId: Int, goalId: Int, goal: GoalDTO.UpdateGoalDTO) {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
val existingGoal =
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
val updatedGoal = existingGoal.copy(
type = goal.type,
name = goal.name,
description = goal.description,
amount = goal.amount,
untilDate = goal.date
)
goalRepo.update(updatedGoal, userId)
}
override fun delete(spaceId: Int, id: Int) {
goalRepo.delete(spaceId, id)
}
override fun getComponents(
spaceId: Int,
goalId: Int
): List<Goal.GoalComponent> {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
return goalRepo.getComponents(spaceId, goalId)
}
override fun getComponent(
spaceId: Int,
goalId: Int,
id: Int
): Goal.GoalComponent? {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
return goalRepo.getComponent(spaceId, goalId, id)
}
override fun createComponent(
spaceId: Int,
goalId: Int,
component: Goal.GoalComponent
): Int {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
return goalRepo.createComponent(goalId, component, userId)
}
override fun updateComponent(
spaceId: Int,
goalId: Int,
component: Goal.GoalComponent
) {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
val existingComponent = goalRepo.getComponent(spaceId, goalId, component.id!!)
?: throw NotFoundException("Component $goalId not found")
val updatedComponent = existingComponent.copy(
name = component.name,
amount = component.amount,
isDone = component.isDone,
date = component.date
)
goalRepo.updateComponent(goalId, updatedComponent.id!!, updatedComponent, userId)
}
override fun deleteComponent(spaceId: Int, goalId: Int, id: Int) {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
goalRepo.getComponent(spaceId, goalId, id) ?: throw NotFoundException("Component $goalId not found")
goalRepo.deleteComponent(goalId, id)
}
override fun assignTransaction(spaceId: Int, goalId: Int, transactionId: Int) {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
transactionRepo.findBySpaceIdAndId(spaceId, transactionId) ?: throw NotFoundException(
"Transaction $transactionId not found"
)
goalRepo.assignTransaction(goalId, transactionId)
}
override fun refuseTransaction(spaceId: Int, goalId: Int, transactionId: Int) {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
transactionRepo.findBySpaceIdAndId(spaceId, transactionId) ?: throw NotFoundException(
"Transaction $transactionId not found"
)
goalRepo.refuseTransaction(goalId, transactionId)
}
}

View File

@@ -110,6 +110,7 @@ class RecurrentOperationServiceImpl(
type = if (it.category?.type == Category.CategoryType.EXPENSE) Transaction.TransactionType.EXPENSE else Transaction.TransactionType.INCOME, type = if (it.category?.type == Category.CategoryType.EXPENSE) Transaction.TransactionType.EXPENSE else Transaction.TransactionType.INCOME,
category = updatedOperation.category, category = updatedOperation.category,
comment = operation.name, comment = operation.name,
amount = updatedOperation.amount,
date = LocalDate.of( date = LocalDate.of(
it.date.year, it.date.year,
it.date.monthValue, it.date.monthValue,

View File

@@ -0,0 +1,22 @@
package space.luminic.finance.services
import space.luminic.finance.dtos.TargetDTO
import space.luminic.finance.models.Target
interface TargetService {
fun findAllBySpaceId(spaceId: Int): List<Target>
fun findBySpaceIdAndId(spaceId: Int, id: Int): Target
fun create(spaceId: Int,target: TargetDTO.CreateTargetDTO): Int
fun update(spaceId: Int, targetId: Int, target: TargetDTO.UpdateTargetDTO)
fun delete(spaceId: Int, id: Int)
fun getComponents(spaceId: Int, targetId: Int): List<Target.TargetComponent>
fun getComponent(spaceId: Int, targetId: Int, id: Int): Target.TargetComponent?
fun createComponent(spaceId: Int, targetId: Int, component: Target.TargetComponent): Int
fun updateComponent(spaceId: Int, targetId: Int, component: Target.TargetComponent)
fun deleteComponent(spaceId: Int, targetId: Int, id: Int)
fun assignTransaction(spaceId: Int, targetId: Int, transactionId: Int)
fun refuseTransaction(spaceId: Int,targetId: Int, transactionId: Int)
}

View File

@@ -0,0 +1,140 @@
package space.luminic.finance.services
import org.springframework.stereotype.Service
import space.luminic.finance.dtos.TargetDTO
import space.luminic.finance.models.Target
import space.luminic.finance.models.NotFoundException
import space.luminic.finance.repos.TargetRepo
import space.luminic.finance.repos.SpaceRepo
import space.luminic.finance.repos.TransactionRepo
@Service
class TargetServiceImpl(
private val targetRepo: TargetRepo,
private val spaceRepo: SpaceRepo,
private val authService: AuthService,
private val transactionRepo: TransactionRepo
) : TargetService {
override fun findAllBySpaceId(spaceId: Int): List<Target> {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
return targetRepo.findAllBySpaceId(spaceId)
}
override fun findBySpaceIdAndId(spaceId: Int, id: Int): Target {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
return targetRepo.findBySpaceIdAndId(spaceId, userId) ?: throw NotFoundException("Goal $id not found")
}
override fun create(spaceId: Int, target: TargetDTO.CreateTargetDTO): Int {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
val creatingTarget = Target(
type = target.type,
name = target.name,
amount = target.amount,
untilDate = target.date
)
return targetRepo.create(creatingTarget, userId)
}
override fun update(spaceId: Int, targetId: Int, target: TargetDTO.UpdateTargetDTO) {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
val existingGoal =
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Goal $targetId not found")
val updatedGoal = existingGoal.copy(
type = target.type,
name = target.name,
description = target.description,
amount = target.amount,
untilDate = target.date
)
targetRepo.update(updatedGoal, userId)
}
override fun delete(spaceId: Int, id: Int) {
targetRepo.delete(spaceId, id)
}
override fun getComponents(
spaceId: Int,
targetId: Int
): List<Target.TargetComponent> {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Goal $targetId not found")
return targetRepo.getComponents(spaceId, targetId)
}
override fun getComponent(
spaceId: Int,
targetId: Int,
id: Int
): Target.TargetComponent? {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Target $targetId not found")
return targetRepo.getComponent(spaceId, targetId, id)
}
override fun createComponent(
spaceId: Int,
targetId: Int,
component: Target.TargetComponent
): Int {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Target $targetId not found")
return targetRepo.createComponent(targetId, component, userId)
}
override fun updateComponent(
spaceId: Int,
targetId: Int,
component: Target.TargetComponent
) {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Target $targetId not found")
val existingComponent = targetRepo.getComponent(spaceId, targetId, component.id!!)
?: throw NotFoundException("Component $targetId not found")
val updatedComponent = existingComponent.copy(
name = component.name,
amount = component.amount,
isDone = component.isDone,
date = component.date
)
targetRepo.updateComponent(targetId, updatedComponent.id!!, updatedComponent, userId)
}
override fun deleteComponent(spaceId: Int, targetId: Int, id: Int) {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Target $targetId not found")
targetRepo.getComponent(spaceId, targetId, id) ?: throw NotFoundException("Component $targetId not found")
targetRepo.deleteComponent(targetId, id)
}
override fun assignTransaction(spaceId: Int, targetId: Int, transactionId: Int) {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Target $targetId not found")
transactionRepo.findBySpaceIdAndId(spaceId, transactionId) ?: throw NotFoundException(
"Transaction $transactionId not found"
)
targetRepo.assignTransaction(targetId, transactionId)
}
override fun refuseTransaction(spaceId: Int, targetId: Int, transactionId: Int) {
val userId = authService.getSecurityUserId()
spaceRepo.findSpaceById(spaceId, userId)
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Target $targetId not found")
transactionRepo.findBySpaceIdAndId(spaceId, transactionId) ?: throw NotFoundException(
"Transaction $transactionId not found"
)
targetRepo.refuseTransaction(targetId, transactionId)
}
}

View File

@@ -60,7 +60,7 @@ class CategorizeService(
listOf( listOf(
InlineKeyboardButton.WebApp( InlineKeyboardButton.WebApp(
"Открыть в WebApp", "Открыть в WebApp",
WebAppInfo("https://app.luminic.space/transactions/${tx.id}/edit") WebAppInfo("https://app.luminic.space/transactions/${tx.id}/edit?mode=from_bot")
) )
) )
) )
@@ -85,7 +85,7 @@ class CategorizeService(
listOf( listOf(
InlineKeyboardButton.WebApp( InlineKeyboardButton.WebApp(
"Открыть в WebApp", "Открыть в WebApp",
WebAppInfo("https://app.luminic.space/transactions/${tx.id}/edit") WebAppInfo("https://app.luminic.space/transactions/${tx.id}/edit?mode=from_bot")
) )
) )
), ),
@@ -131,7 +131,7 @@ class CategorizeService(
listOf( listOf(
InlineKeyboardButton.WebApp( InlineKeyboardButton.WebApp(
"Открыть в WebApp", "Открыть в WebApp",
WebAppInfo("https://app.luminic.space/transactions/${tx.id}/edit") WebAppInfo("https://app.luminic.space/transactions/${tx.id}/edit?mode=from_bot")
) )
) )
), ),

View File

@@ -119,7 +119,7 @@ class BotService(
@Bean @Bean
fun bot(): Bot { fun bot(): Bot {
val bot = com.github.kotlintelegrambot.bot { val bot = com.github.kotlintelegrambot.bot {
logLevel = LogLevel.All() logLevel = LogLevel.None
token = botToken token = botToken
dispatch { dispatch {
message(Filter.Text) { message(Filter.Text) {

View File

@@ -0,0 +1,59 @@
DROP table if exists finance.goals cascade;
DROP table if exists finance.goals_components cascade;
DROP table if exists finance.goals_transactions cascade;
DROP table if exists finance.targets cascade;
DROP table if exists finance.targets_components cascade;
DROP table if exists finance.targets_transactions cascade;
CREATE TABLE finance.targets
(
id integer generated by default as identity not null,
space_id INTEGER,
type SMALLINT,
name VARCHAR(255),
description VARCHAR(255),
amount DECIMAL,
until_date date,
created_by_id INTEGER,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_by_id INTEGER,
updated_at TIMESTAMP WITHOUT TIME ZONE,
CONSTRAINT pk_targets PRIMARY KEY (id)
);
ALTER TABLE finance.targets
ADD CONSTRAINT FK_TARGETS_ON_CREATEDBY FOREIGN KEY (created_by_id) REFERENCES finance.users (id);
ALTER TABLE finance.targets
ADD CONSTRAINT FK_TARGETS_ON_SPACE FOREIGN KEY (space_id) REFERENCES finance.spaces (id);
ALTER TABLE finance.targets
ADD CONSTRAINT FK_TARGETS_ON_UPDATEDBY FOREIGN KEY (updated_by_id) REFERENCES finance.users (id);
CREATE TABLE finance.targets_transactions
(
target_id INTEGER NOT NULL,
transactions_id INTEGER NOT NULL
);
ALTER TABLE finance.targets_transactions
ADD CONSTRAINT fk_targettx_on_target FOREIGN KEY (target_id) REFERENCES finance.targets (id);
ALTER TABLE finance.targets_transactions
ADD CONSTRAINT fk_targettx_on_tx FOREIGN KEY (transactions_id) REFERENCES finance.transactions (id);
create table if not exists finance.targets_components
(
id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL,
target_id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
amount NUMERIC NOT NULL,
is_done BOOLEAN NOT NULL DEFAULT FALSE,
date TIMESTAMP WITH TIME ZONE NULL
);
alter table finance.targets_components
add constraint fk_target_on_components foreign key (target_id) references finance.targets (id);