WIP: Implement recurring transactions
Signed-off-by: William Brawner <me@wbrawner.com>
This commit is contained in:
parent
36ee1bccfa
commit
f9a1521d65
10 changed files with 625 additions and 16 deletions
|
@ -2,7 +2,6 @@ package com.wbrawner.budgetserver.budget;
|
|||
|
||||
import com.wbrawner.budgetserver.permission.UserPermissionRequest;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
@ -23,7 +22,6 @@ public class BudgetRequest {
|
|||
this.users.addAll(users);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<UserPermissionRequest> getUsers() {
|
||||
return Set.copyOf(users);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
package com.wbrawner.budgetserver.recurrence;
|
||||
|
||||
import com.wbrawner.budgetserver.budget.Budget;
|
||||
import com.wbrawner.budgetserver.category.Category;
|
||||
import com.wbrawner.budgetserver.user.User;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static com.wbrawner.budgetserver.Utils.randomId;
|
||||
|
||||
@Entity
|
||||
public class RecurringTransaction {
|
||||
@Id
|
||||
private final String id = randomId();
|
||||
@ManyToOne
|
||||
@JoinColumn(nullable = false)
|
||||
private final User createdBy;
|
||||
private String title;
|
||||
private String description;
|
||||
private Long amount;
|
||||
@ManyToOne
|
||||
private Category category;
|
||||
private Boolean expense;
|
||||
@ManyToOne
|
||||
@JoinColumn(nullable = false)
|
||||
private Budget budget;
|
||||
private String timeZone;
|
||||
private int time;
|
||||
private FrequencyUnit frequencyUnit;
|
||||
private int frequencyValue;
|
||||
|
||||
public RecurringTransaction() {
|
||||
this(
|
||||
null,
|
||||
null,
|
||||
FrequencyUnit.DAILY,
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public RecurringTransaction(
|
||||
String title,
|
||||
String description,
|
||||
FrequencyUnit frequencyUnit,
|
||||
int frequencyValue,
|
||||
String timeZone,
|
||||
int time,
|
||||
Long amount,
|
||||
Category category,
|
||||
Boolean expense,
|
||||
User createdBy,
|
||||
Budget budget
|
||||
) {
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.frequencyUnit = frequencyUnit;
|
||||
this.frequencyValue = frequencyValue;
|
||||
setTimeZone(timeZone);
|
||||
this.time = time;
|
||||
this.amount = amount;
|
||||
this.category = category;
|
||||
this.expense = expense;
|
||||
this.createdBy = createdBy;
|
||||
this.budget = budget;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public FrequencyUnit getFrequencyUnit() {
|
||||
return frequencyUnit;
|
||||
}
|
||||
|
||||
public int getFrequencyValue() {
|
||||
return frequencyValue;
|
||||
}
|
||||
|
||||
public void setFrequency(FrequencyUnit frequencyUnit, int frequencyValue) {
|
||||
if (frequencyValue < 0) throw new IllegalArgumentException("frequencyValue must be at least 0");
|
||||
if (frequencyValue > frequencyUnit.maxValue) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Invalid frequencyValue. Requested %d for %s but maxValue is %d",
|
||||
frequencyValue,
|
||||
frequencyUnit.name(),
|
||||
frequencyUnit.maxValue
|
||||
));
|
||||
}
|
||||
this.frequencyUnit = frequencyUnit;
|
||||
this.frequencyValue = frequencyValue;
|
||||
}
|
||||
|
||||
public String getTimeZone() {
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
public void setTimeZone(String timeZone) {
|
||||
this.timeZone = TimeZone.getTimeZone(timeZone).getID();
|
||||
}
|
||||
|
||||
public int getTimeOfDayInSeconds() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTimeOfDayInSeconds(int time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public Long getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(Long amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public Category getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(Category category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public Boolean isExpense() {
|
||||
return expense;
|
||||
}
|
||||
|
||||
public void setExpense(Boolean expense) {
|
||||
this.expense = expense;
|
||||
}
|
||||
|
||||
public User getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public Budget getBudget() {
|
||||
return budget;
|
||||
}
|
||||
|
||||
public void setBudget(Budget budget) {
|
||||
this.budget = budget;
|
||||
}
|
||||
|
||||
enum FrequencyUnit {
|
||||
DAILY(0),
|
||||
WEEKLY(7),
|
||||
MONTHLY(30),
|
||||
YEARLY(365);
|
||||
|
||||
int maxValue;
|
||||
|
||||
FrequencyUnit(int maxValue) {
|
||||
this.maxValue = maxValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package com.wbrawner.budgetserver.recurrence;
|
||||
|
||||
import com.wbrawner.budgetserver.ErrorResponse;
|
||||
import com.wbrawner.budgetserver.category.Category;
|
||||
import com.wbrawner.budgetserver.category.CategoryRepository;
|
||||
import com.wbrawner.budgetserver.permission.Permission;
|
||||
import com.wbrawner.budgetserver.permission.UserPermission;
|
||||
import com.wbrawner.budgetserver.permission.UserPermissionRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.wbrawner.budgetserver.Utils.getCurrentUser;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(path = "/recurrence")
|
||||
@Transactional
|
||||
public class RecurringTransactionController {
|
||||
private final CategoryRepository categoryRepository;
|
||||
private final RecurringTransactionRepository recurringTransactionRepository;
|
||||
private final UserPermissionRepository userPermissionsRepository;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RecurringTransactionController.class);
|
||||
|
||||
public RecurringTransactionController(
|
||||
CategoryRepository categoryRepository,
|
||||
RecurringTransactionRepository recurringTransactionRepository,
|
||||
UserPermissionRepository userPermissionsRepository
|
||||
) {
|
||||
this.categoryRepository = categoryRepository;
|
||||
this.recurringTransactionRepository = recurringTransactionRepository;
|
||||
this.userPermissionsRepository = userPermissionsRepository;
|
||||
}
|
||||
|
||||
@GetMapping(path = "", produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
public ResponseEntity<List<RecurringTransactionResponse>> getRecurringTransactions(
|
||||
@RequestParam("budgetId") String budgetId
|
||||
) {
|
||||
List<UserPermission> userPermissions = userPermissionsRepository.findAllByUserAndBudget_IdIn(
|
||||
getCurrentUser(),
|
||||
Collections.singletonList(budgetId),
|
||||
null
|
||||
);
|
||||
if (userPermissions.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||
}
|
||||
var budget = userPermissions.get(0).getBudget();
|
||||
var transactions = recurringTransactionRepository.findAllByBudget(budget)
|
||||
.stream()
|
||||
.map(RecurringTransactionResponse::new)
|
||||
.collect(Collectors.toList());
|
||||
return ResponseEntity.ok(transactions);
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{id}", produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
public ResponseEntity<RecurringTransactionResponse> getRecurringTransaction(@PathVariable String id) {
|
||||
var budgets = userPermissionsRepository.findAllByUser(getCurrentUser(), null)
|
||||
.stream()
|
||||
.map(UserPermission::getBudget)
|
||||
.collect(Collectors.toList());
|
||||
var transaction = recurringTransactionRepository.findByIdAndBudgetIn(id, budgets).orElse(null);
|
||||
if (transaction == null) return ResponseEntity.notFound().build();
|
||||
return ResponseEntity.ok(new RecurringTransactionResponse(transaction));
|
||||
}
|
||||
|
||||
@PostMapping(path = "", consumes = {MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
public ResponseEntity<Object> newTransaction(@RequestBody RecurringTransactionRequest request) {
|
||||
var userResponse = userPermissionsRepository.findByUserAndBudget_Id(getCurrentUser(), request.getBudgetId())
|
||||
.orElse(null);
|
||||
if (userResponse == null) {
|
||||
return ResponseEntity.badRequest().body(new ErrorResponse("Invalid budget ID"));
|
||||
}
|
||||
if (userResponse.getPermission().isNotAtLeast(Permission.WRITE)) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||
}
|
||||
var budget = userResponse.getBudget();
|
||||
Category category = null;
|
||||
if (request.getCategoryId() != null) {
|
||||
category = categoryRepository.findByBudgetAndId(budget, request.getCategoryId()).orElse(null);
|
||||
}
|
||||
return ResponseEntity.ok(new RecurringTransactionResponse(recurringTransactionRepository.save(new RecurringTransaction(
|
||||
request.getTitle(),
|
||||
request.getDescription(),
|
||||
request.getFrequencyUnit(),
|
||||
request.getFrequencyValue(),
|
||||
request.getTimeZone(),
|
||||
request.getTime(),
|
||||
request.getAmount(),
|
||||
category,
|
||||
request.getExpense(),
|
||||
getCurrentUser(),
|
||||
budget
|
||||
))));
|
||||
}
|
||||
|
||||
@PutMapping(path = "/{id}", consumes = {MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
public ResponseEntity<Object> updateTransaction(@PathVariable String id, @RequestBody RecurringTransactionRequest request) {
|
||||
var transaction = recurringTransactionRepository.findById(id).orElse(null);
|
||||
if (transaction == null) return ResponseEntity.notFound().build();
|
||||
var userPermission = userPermissionsRepository.findByUserAndBudget_Id(getCurrentUser(), transaction.getBudget().getId()).orElse(null);
|
||||
if (userPermission == null) return ResponseEntity.notFound().build();
|
||||
if (userPermission.getPermission().isNotAtLeast(Permission.WRITE)) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||
}
|
||||
if (request.getTitle() != null) {
|
||||
transaction.setTitle(request.getTitle());
|
||||
}
|
||||
if (request.getDescription() != null) {
|
||||
transaction.setDescription(request.getDescription());
|
||||
}
|
||||
if (request.getTimeZone() != null) {
|
||||
transaction.setTimeZone(request.getTimeZone());
|
||||
}
|
||||
if (request.getAmount() != null) {
|
||||
transaction.setAmount(request.getAmount());
|
||||
}
|
||||
if (request.getExpense() != null) {
|
||||
transaction.setExpense(request.getExpense());
|
||||
}
|
||||
if (request.getTime() != null) {
|
||||
transaction.setTimeOfDayInSeconds(request.getTime());
|
||||
}
|
||||
if (request.getFrequencyUnit() != null && request.getFrequencyValue() != null) {
|
||||
transaction.setFrequency(request.getFrequencyUnit(), request.getFrequencyValue());
|
||||
}
|
||||
if (request.getBudgetId() != null) {
|
||||
var newUserPermission = userPermissionsRepository.findByUserAndBudget_Id(getCurrentUser(), request.getBudgetId()).orElse(null);
|
||||
if (newUserPermission == null || newUserPermission.getPermission().isNotAtLeast(Permission.WRITE)) {
|
||||
return ResponseEntity
|
||||
.badRequest()
|
||||
.body(new ErrorResponse("Invalid budget"));
|
||||
}
|
||||
transaction.setBudget(newUserPermission.getBudget());
|
||||
}
|
||||
if (request.getCategoryId() != null) {
|
||||
var category = categoryRepository.findByBudgetAndId(transaction.getBudget(), request.getCategoryId()).orElse(null);
|
||||
if (category == null) {
|
||||
return ResponseEntity
|
||||
.badRequest()
|
||||
.body(new ErrorResponse("Invalid category"));
|
||||
}
|
||||
transaction.setCategory(category);
|
||||
}
|
||||
return ResponseEntity.ok(new RecurringTransactionResponse(recurringTransactionRepository.save(transaction)));
|
||||
}
|
||||
|
||||
@DeleteMapping(path = "/{id}", produces = {MediaType.TEXT_PLAIN_VALUE})
|
||||
public ResponseEntity<Void> deleteTransaction(@PathVariable String id) {
|
||||
var transaction = recurringTransactionRepository.findById(id).orElse(null);
|
||||
if (transaction == null) return ResponseEntity.notFound().build();
|
||||
// Check that the transaction belongs to an budget that the user has access to before deleting it
|
||||
var userPermission = userPermissionsRepository.findByUserAndBudget_Id(getCurrentUser(), transaction.getBudget().getId()).orElse(null);
|
||||
if (userPermission == null) return ResponseEntity.notFound().build();
|
||||
if (userPermission.getPermission().isNotAtLeast(Permission.WRITE)) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||
}
|
||||
recurringTransactionRepository.delete(transaction);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.wbrawner.budgetserver.recurrence;
|
||||
|
||||
import com.wbrawner.budgetserver.budget.Budget;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface RecurringTransactionRepository extends PagingAndSortingRepository<RecurringTransaction, String> {
|
||||
Optional<RecurringTransaction> findByIdAndBudgetIn(String id, List<Budget> budgets);
|
||||
|
||||
List<RecurringTransaction> findAllByBudget(Budget budget);
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package com.wbrawner.budgetserver.recurrence;
|
||||
|
||||
class RecurringTransactionRequest {
|
||||
private final String title;
|
||||
private final String description;
|
||||
private final Long amount;
|
||||
private final String categoryId;
|
||||
private final Boolean expense;
|
||||
private final String budgetId;
|
||||
private final String timeZone;
|
||||
private final Integer time;
|
||||
private final RecurringTransaction.FrequencyUnit frequencyUnit;
|
||||
private final Integer frequencyValue;
|
||||
|
||||
|
||||
RecurringTransactionRequest() {
|
||||
this(null, null, null, 0, null, 0, null, null, null, null);
|
||||
}
|
||||
|
||||
RecurringTransactionRequest(
|
||||
String title,
|
||||
String description,
|
||||
RecurringTransaction.FrequencyUnit frequencyUnit,
|
||||
int frequencyValue,
|
||||
String timeZone,
|
||||
int time,
|
||||
Long amount,
|
||||
String categoryId,
|
||||
Boolean expense,
|
||||
String budgetId
|
||||
) {
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.frequencyUnit = frequencyUnit;
|
||||
this.frequencyValue = frequencyValue;
|
||||
this.timeZone = timeZone;
|
||||
this.time = time;
|
||||
this.amount = amount;
|
||||
this.categoryId = categoryId;
|
||||
this.expense = expense;
|
||||
this.budgetId = budgetId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getTimeZone() {
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
public Integer getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public RecurringTransaction.FrequencyUnit getFrequencyUnit() {
|
||||
return frequencyUnit;
|
||||
}
|
||||
|
||||
public Integer getFrequencyValue() {
|
||||
return frequencyValue;
|
||||
}
|
||||
|
||||
public Long getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public String getCategoryId() {
|
||||
return categoryId;
|
||||
}
|
||||
|
||||
public Boolean getExpense() {
|
||||
return expense;
|
||||
}
|
||||
|
||||
public String getBudgetId() {
|
||||
return budgetId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.wbrawner.budgetserver.recurrence;
|
||||
|
||||
class RecurringTransactionResponse {
|
||||
public final String id;
|
||||
public final String title;
|
||||
public final String description;
|
||||
public final long amount;
|
||||
public final boolean expense;
|
||||
public final String budgetId;
|
||||
public final String categoryId;
|
||||
public final String createdBy;
|
||||
public final String timeZone;
|
||||
public final int time;
|
||||
public final RecurringTransaction.FrequencyUnit frequencyUnit;
|
||||
public final int frequencyValue;
|
||||
|
||||
|
||||
RecurringTransactionResponse(
|
||||
String id,
|
||||
String title,
|
||||
String description,
|
||||
long amount,
|
||||
boolean expense,
|
||||
String budgetId,
|
||||
String categoryId,
|
||||
String createdBy,
|
||||
String timeZone,
|
||||
int time,
|
||||
RecurringTransaction.FrequencyUnit frequencyUnit,
|
||||
int frequencyValue
|
||||
) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.amount = amount;
|
||||
this.expense = expense;
|
||||
this.budgetId = budgetId;
|
||||
this.categoryId = categoryId;
|
||||
this.createdBy = createdBy;
|
||||
this.timeZone = timeZone;
|
||||
this.time = time;
|
||||
this.frequencyUnit = frequencyUnit;
|
||||
this.frequencyValue = frequencyValue;
|
||||
}
|
||||
|
||||
RecurringTransactionResponse(RecurringTransaction recurringTransaction) {
|
||||
this(
|
||||
recurringTransaction.getId(),
|
||||
recurringTransaction.getTitle(),
|
||||
recurringTransaction.getDescription(),
|
||||
recurringTransaction.getAmount(),
|
||||
recurringTransaction.isExpense(),
|
||||
recurringTransaction.getBudget().getId(),
|
||||
recurringTransaction.getCategory() != null ? recurringTransaction.getCategory().getId() : null,
|
||||
recurringTransaction.getCreatedBy().getId(),
|
||||
recurringTransaction.getTimeZone(),
|
||||
recurringTransaction.getTimeOfDayInSeconds(),
|
||||
recurringTransaction.getFrequencyUnit(),
|
||||
recurringTransaction.getFrequencyValue()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package com.wbrawner.budgetserver.recurrence;
|
||||
|
||||
import com.wbrawner.budgetserver.transaction.Transaction;
|
||||
import com.wbrawner.budgetserver.transaction.TransactionRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@Component
|
||||
public class RecurringTransactionTask {
|
||||
private final RecurringTransactionRepository recurringTransactionRepository;
|
||||
private final TransactionRepository transactionRepository;
|
||||
|
||||
@Autowired
|
||||
public RecurringTransactionTask(
|
||||
RecurringTransactionRepository recurringTransactionRepository,
|
||||
TransactionRepository transactionRepository
|
||||
) {
|
||||
this.recurringTransactionRepository = recurringTransactionRepository;
|
||||
this.transactionRepository = transactionRepository;
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 * * * *")
|
||||
public void createTransactions() {
|
||||
recurringTransactionRepository.findAll().forEach(recurringTransaction -> {
|
||||
GregorianCalendar today = new GregorianCalendar(TimeZone.getTimeZone(recurringTransaction.getTimeZone()));
|
||||
// if recurrence matches today, create a new transaction
|
||||
int adjustedFrequencyValue, calendarField;
|
||||
switch (recurringTransaction.getFrequencyUnit()) {
|
||||
case DAILY -> {
|
||||
// Daily transactions should have the frequency value set to 0, so we just force it to be the same
|
||||
// as the current date in order to force the transaction creation.
|
||||
adjustedFrequencyValue = today.get(Calendar.DATE);
|
||||
calendarField = Calendar.DATE;
|
||||
}
|
||||
case WEEKLY -> {
|
||||
// No adjustments needed for day of week
|
||||
adjustedFrequencyValue = today.get(Calendar.DAY_OF_WEEK);
|
||||
calendarField = Calendar.DAY_OF_WEEK;
|
||||
}
|
||||
case MONTHLY -> {
|
||||
// Check if the day of the month is correct
|
||||
adjustedFrequencyValue = Math.min(recurringTransaction.getFrequencyValue(), today.getActualMaximum(Calendar.DAY_OF_MONTH));
|
||||
calendarField = Calendar.DAY_OF_MONTH;
|
||||
}
|
||||
case YEARLY -> {
|
||||
adjustedFrequencyValue = recurringTransaction.getFrequencyValue();
|
||||
if (today.isLeapYear(today.get(Calendar.YEAR)) && today.get(Calendar.DAY_OF_YEAR) >= 31 + 29) {
|
||||
// We're just pretending that Feb 29th doesn't exist here...
|
||||
adjustedFrequencyValue -= 1;
|
||||
}
|
||||
calendarField = Calendar.DAY_OF_YEAR;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected value: " + recurringTransaction.getFrequencyUnit());
|
||||
}
|
||||
if (adjustedFrequencyValue == today.get(calendarField)) {
|
||||
createTransaction(recurringTransaction, today);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createTransaction(RecurringTransaction recurringTransaction, Calendar transactionCalendar) {
|
||||
transactionCalendar.set(Calendar.HOUR, 0);
|
||||
transactionCalendar.set(Calendar.MINUTE, 0);
|
||||
transactionCalendar.set(Calendar.SECOND, 0);
|
||||
transactionCalendar.set(Calendar.MILLISECOND, 0);
|
||||
transactionCalendar.add(Calendar.SECOND, recurringTransaction.getTimeOfDayInSeconds());
|
||||
transactionRepository.save(new Transaction(
|
||||
recurringTransaction.getTitle(),
|
||||
recurringTransaction.getDescription(),
|
||||
transactionCalendar.toInstant(),
|
||||
recurringTransaction.getAmount(),
|
||||
recurringTransaction.getCategory(),
|
||||
recurringTransaction.isExpense(),
|
||||
recurringTransaction.getCreatedBy(),
|
||||
recurringTransaction.getBudget(),
|
||||
recurringTransaction
|
||||
));
|
||||
}
|
||||
}
|
|
@ -2,9 +2,13 @@ package com.wbrawner.budgetserver.transaction;
|
|||
|
||||
import com.wbrawner.budgetserver.budget.Budget;
|
||||
import com.wbrawner.budgetserver.category.Category;
|
||||
import com.wbrawner.budgetserver.recurrence.RecurringTransaction;
|
||||
import com.wbrawner.budgetserver.user.User;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import java.time.Instant;
|
||||
|
||||
import static com.wbrawner.budgetserver.Utils.randomId;
|
||||
|
@ -26,19 +30,24 @@ public class Transaction implements Comparable<Transaction> {
|
|||
@ManyToOne
|
||||
@JoinColumn(nullable = false)
|
||||
private Budget budget;
|
||||
@ManyToOne
|
||||
private RecurringTransaction recurrence;
|
||||
|
||||
public Transaction() {
|
||||
this(null, null, null, null, null, null, null, null);
|
||||
this(null, null, null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public Transaction(String title,
|
||||
public Transaction(
|
||||
String title,
|
||||
String description,
|
||||
Instant date,
|
||||
Long amount,
|
||||
Category category,
|
||||
Boolean expense,
|
||||
User createdBy,
|
||||
Budget budget) {
|
||||
Budget budget,
|
||||
RecurringTransaction recurrence
|
||||
) {
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.date = date;
|
||||
|
@ -47,11 +56,10 @@ public class Transaction implements Comparable<Transaction> {
|
|||
this.expense = expense;
|
||||
this.createdBy = createdBy;
|
||||
this.budget = budget;
|
||||
this.recurrence = recurrence;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
// This should only be set from Hibernate so it shouldn't actually be null ever
|
||||
//noinspection ConstantConditions
|
||||
return id;
|
||||
}
|
||||
|
||||
|
@ -115,6 +123,14 @@ public class Transaction implements Comparable<Transaction> {
|
|||
this.budget = budget;
|
||||
}
|
||||
|
||||
public RecurringTransaction getRecurrence() {
|
||||
return this.recurrence;
|
||||
}
|
||||
|
||||
public void setRecurrence(RecurringTransaction recurrence) {
|
||||
this.recurrence = recurrence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Transaction other) {
|
||||
return this.date.compareTo(other.date);
|
||||
|
|
|
@ -144,7 +144,8 @@ public class TransactionController {
|
|||
category,
|
||||
request.getExpense(),
|
||||
getCurrentUser(),
|
||||
budget
|
||||
budget,
|
||||
null
|
||||
))));
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ buildscript {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:2.2.2.RELEASE"
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:2.4.3"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue