Implement category archiving

Archiving is a step below deleting. It represents categories that may no
longer be relevant to your current budget, but are still useful to keep
around for historical reasons.
This commit is contained in:
William Brawner 2020-06-30 03:18:24 +00:00
parent fd517cb2f8
commit 05f203a56f
6 changed files with 41 additions and 15 deletions

View file

@ -16,6 +16,8 @@ public class Category implements Comparable<Category> {
@ManyToOne @ManyToOne
private Budget budget; private Budget budget;
private boolean expense; private boolean expense;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean archived;
public Category() { public Category() {
this(null, null, 0L, null, true); this(null, null, 0L, null, true);
@ -83,4 +85,12 @@ public class Category implements Comparable<Category> {
public void setExpense(boolean expense) { public void setExpense(boolean expense) {
this.expense = expense; this.expense = expense;
} }
public boolean isArchived() {
return archived;
}
public void setArchived(boolean archived) {
this.archived = archived;
}
} }

View file

@ -8,6 +8,9 @@ import com.wbrawner.budgetserver.transaction.TransactionRepository;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.Authorization; import io.swagger.annotations.Authorization;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -44,6 +47,7 @@ class CategoryController {
ResponseEntity<List<CategoryResponse>> getCategories( ResponseEntity<List<CategoryResponse>> getCategories(
@RequestParam(name = "budgetIds", required = false) List<Long> budgetIds, @RequestParam(name = "budgetIds", required = false) List<Long> budgetIds,
@RequestParam(name = "isExpense", required = false) Boolean isExpense, @RequestParam(name = "isExpense", required = false) Boolean isExpense,
@RequestParam(name = "includeArchived", required = false) Boolean includeArchived,
@RequestParam(name = "count", required = false) Integer count, @RequestParam(name = "count", required = false) Integer count,
@RequestParam(name = "page", required = false) Integer page, @RequestParam(name = "page", required = false) Integer page,
@RequestParam(name = "false", required = false) String sortBy, @RequestParam(name = "false", required = false) String sortBy,
@ -69,13 +73,8 @@ class CategoryController {
sortOrder != null ? sortOrder : Sort.Direction.ASC, sortOrder != null ? sortOrder : Sort.Direction.ASC,
sortBy != null ? sortBy : "title" sortBy != null ? sortBy : "title"
); );
List<Category> categories; Boolean archived = includeArchived == null || includeArchived == false ? false : null;
if (isExpense == null) { List<Category> categories = categoryRepository.findAllByBudgetIn(budgets, isExpense, archived, pageRequest);
categories = categoryRepository.findAllByBudgetIn(budgets, pageRequest);
} else {
categories = categoryRepository.findAllByBudgetInAndExpense(budgets, isExpense, pageRequest);
}
return ResponseEntity.ok( return ResponseEntity.ok(
categories.stream() categories.stream()
.map(CategoryResponse::new) .map(CategoryResponse::new)
@ -153,6 +152,9 @@ class CategoryController {
if (request.getExpense() != null) { if (request.getExpense() != null) {
category.setExpense(request.getExpense()); category.setExpense(request.getExpense());
} }
if (request.getArchived() != null) {
category.setArchived(request.getArchived());
}
return ResponseEntity.ok(new CategoryResponse(categoryRepository.save(category))); return ResponseEntity.ok(new CategoryResponse(categoryRepository.save(category)));
} }

View file

@ -1,7 +1,9 @@
package com.wbrawner.budgetserver.category; package com.wbrawner.budgetserver.category;
import com.wbrawner.budgetserver.budget.Budget; import com.wbrawner.budgetserver.budget.Budget;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List; import java.util.List;
@ -10,12 +12,11 @@ import java.util.Optional;
public interface CategoryRepository extends PagingAndSortingRepository<Category, Long> { public interface CategoryRepository extends PagingAndSortingRepository<Category, Long> {
List<Category> findAllByBudget(Budget budget, Pageable pageable); List<Category> findAllByBudget(Budget budget, Pageable pageable);
List<Category> findAllByBudgetIn(List<Budget> budgets, Pageable pageable); @Query("SELECT c FROM Category c where c.budget IN (:budgets) AND (:expense IS NULL OR c.expense = :expense) AND (:archived IS NULL OR c.archived = :archived)")
List<Category> findAllByBudgetIn(List<Budget> budgets, Boolean expense, Boolean archived, Pageable pageable);
Optional<Category> findByBudgetInAndId(List<Budget> budgets, Long id); Optional<Category> findByBudgetInAndId(List<Budget> budgets, Long id);
List<Category> findAllByBudgetInAndExpense(List<Budget> budgets, Boolean isExpense, Pageable pageable);
Optional<Category> findByBudgetAndId(Budget budget, Long id); Optional<Category> findByBudgetAndId(Budget budget, Long id);
List<Category> findAllByBudgetInAndIdIn(List<Budget> budgets, List<Long> ids, Pageable pageable); List<Category> findAllByBudgetInAndIdIn(List<Budget> budgets, List<Long> ids, Pageable pageable);

View file

@ -9,6 +9,7 @@ public class CategoryResponse {
private final long amount; private final long amount;
private final long budgetId; private final long budgetId;
private final boolean expense; private final boolean expense;
private final boolean archived;
public CategoryResponse(Category category) { public CategoryResponse(Category category) {
this( this(
@ -17,17 +18,19 @@ public class CategoryResponse {
category.getDescription(), category.getDescription(),
category.getAmount(), category.getAmount(),
Objects.requireNonNull(category.getBudget()).getId(), Objects.requireNonNull(category.getBudget()).getId(),
category.isExpense() category.isExpense(),
category.isArchived()
); );
} }
public CategoryResponse(long id, String title, String description, long amount, long budgetId, boolean expense) { public CategoryResponse(long id, String title, String description, long amount, long budgetId, boolean expense, boolean archived) {
this.id = id; this.id = id;
this.title = title; this.title = title;
this.description = description; this.description = description;
this.amount = amount; this.amount = amount;
this.budgetId = budgetId; this.budgetId = budgetId;
this.expense = expense; this.expense = expense;
this.archived = archived;
} }
public long getId() { public long getId() {
@ -53,4 +56,8 @@ public class CategoryResponse {
public boolean isExpense() { public boolean isExpense() {
return expense; return expense;
} }
public boolean isArchived() {
return archived;
}
} }

View file

@ -5,16 +5,18 @@ public class UpdateCategoryRequest {
private final String description; private final String description;
private final Long amount; private final Long amount;
private final Boolean expense; private final Boolean expense;
private final Boolean archived;
public UpdateCategoryRequest() { public UpdateCategoryRequest() {
this(null, null, null, null); this(null, null, null, null, null);
} }
public UpdateCategoryRequest(String title, String description, Long amount, Boolean expense) { public UpdateCategoryRequest(String title, String description, Long amount, Boolean expense, Boolean archived) {
this.title = title; this.title = title;
this.description = description; this.description = description;
this.amount = amount; this.amount = amount;
this.expense = expense; this.expense = expense;
this.archived = archived;
} }
public String getTitle() { public String getTitle() {
@ -32,4 +34,8 @@ public class UpdateCategoryRequest {
public Boolean getExpense() { public Boolean getExpense() {
return expense; return expense;
} }
public Boolean getArchived() {
return archived;
}
} }

View file

@ -74,7 +74,7 @@ public class TransactionController {
if (categoryIds != null && !categoryIds.isEmpty()) { if (categoryIds != null && !categoryIds.isEmpty()) {
categories = categoryRepository.findAllByBudgetInAndIdIn(budgets, categoryIds, null); categories = categoryRepository.findAllByBudgetInAndIdIn(budgets, categoryIds, null);
} else { } else {
categories = categoryRepository.findAllByBudgetIn(budgets, null); categories = categoryRepository.findAllByBudgetIn(budgets, null, null, null);
} }
var pageRequest = PageRequest.of( var pageRequest = PageRequest.of(
Math.min(0, page != null ? page - 1 : 0), Math.min(0, page != null ? page - 1 : 0),