fix: changed Expense relation with Person entity
This commit is contained in:
parent
13d5f1bf29
commit
6475f83dd8
5 changed files with 73 additions and 63 deletions
|
@ -8,7 +8,6 @@ import lombok.Setter;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -45,19 +44,13 @@ public class Expense extends AbstractEntity {
|
||||||
@Column(name = "PeriodInterval")
|
@Column(name = "PeriodInterval")
|
||||||
private Integer periodInterval;
|
private Integer periodInterval;
|
||||||
|
|
||||||
@ManyToMany(fetch = FetchType.EAGER)
|
@ManyToOne
|
||||||
@JoinTable(
|
@JoinColumn(name = "CreditorId")
|
||||||
name = "Creditor_expenses",
|
private Person creditor;
|
||||||
joinColumns = @JoinColumn(name = "expense_id"),
|
|
||||||
inverseJoinColumns = @JoinColumn(name = "people_id"))
|
|
||||||
private Set<Person> creditors;
|
|
||||||
|
|
||||||
@ManyToMany(fetch = FetchType.EAGER)
|
@ManyToOne
|
||||||
@JoinTable(
|
@JoinColumn(name = "DebtorId")
|
||||||
name = "Debtors_expenses",
|
private Person debtor;
|
||||||
joinColumns = @JoinColumn(name = "expense_id"),
|
|
||||||
inverseJoinColumns = @JoinColumn(name = "people_id"))
|
|
||||||
private Set<Person> debtors;
|
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "EventId")
|
@JoinColumn(name = "EventId")
|
||||||
|
@ -69,9 +62,6 @@ public class Expense extends AbstractEntity {
|
||||||
@Column(name = "PaymentDate")
|
@Column(name = "PaymentDate")
|
||||||
private LocalDateTime paymentDate;
|
private LocalDateTime paymentDate;
|
||||||
|
|
||||||
/**
|
|
||||||
* the isPaid field starts as always false
|
|
||||||
*/
|
|
||||||
@Column(name = "isPaid", nullable = false)
|
@Column(name = "isPaid", nullable = false)
|
||||||
private Boolean isPaid = false;
|
private Boolean isPaid = false;
|
||||||
|
|
||||||
|
|
|
@ -40,11 +40,11 @@ public class Person extends AbstractEntity {
|
||||||
@Column(name = "credit")
|
@Column(name = "credit")
|
||||||
private BigDecimal credit;
|
private BigDecimal credit;
|
||||||
|
|
||||||
@ManyToMany(mappedBy = "creditors")
|
@OneToMany(mappedBy = "creditor")
|
||||||
private Set<Expense> creditorExpenses;
|
private Set<Expense> creditExpenses;
|
||||||
|
|
||||||
@ManyToMany(mappedBy = "debtors")
|
@OneToMany(mappedBy = "debtor")
|
||||||
private Set<Expense> debtorExpenses;
|
private Set<Expense> debtExpenses;
|
||||||
|
|
||||||
@ManyToMany(mappedBy = "participants")
|
@ManyToMany(mappedBy = "participants")
|
||||||
private Set<Event> events;
|
private Set<Event> events;
|
||||||
|
|
|
@ -13,24 +13,33 @@ import java.util.Set;
|
||||||
|
|
||||||
public interface ExpenseRepository extends JpaRepository<Expense, Long>, JpaSpecificationExecutor<Expense> {
|
public interface ExpenseRepository extends JpaRepository<Expense, Long>, JpaSpecificationExecutor<Expense> {
|
||||||
|
|
||||||
@Query("SELECT e FROM Expense e JOIN e.creditors c WHERE c.id = :personId")
|
// Find expenses where the creditor is a specific person
|
||||||
|
@Query("SELECT e FROM Expense e WHERE e.creditor.id = :personId")
|
||||||
Set<Expense> findCreditorsExpensesByPersonId(@Param("personId") Long personId);
|
Set<Expense> findCreditorsExpensesByPersonId(@Param("personId") Long personId);
|
||||||
|
|
||||||
@Query("SELECT e FROM Expense e JOIN e.debtors d WHERE d.id = :personId")
|
// Find expenses where the debtor is a specific person
|
||||||
|
@Query("SELECT e FROM Expense e WHERE e.debtor.id = :personId")
|
||||||
Set<Expense> findDebtorsExpensesByPersonId(@Param("personId") Long personId);
|
Set<Expense> findDebtorsExpensesByPersonId(@Param("personId") Long personId);
|
||||||
|
|
||||||
|
// Find all expenses for a given year
|
||||||
@Query("SELECT e FROM Expense e WHERE YEAR(e.date) = :year")
|
@Query("SELECT e FROM Expense e WHERE YEAR(e.date) = :year")
|
||||||
List<Expense> findAllByYear(@Param("year") int year);
|
List<Expense> findAllByYear(@Param("year") int year);
|
||||||
|
|
||||||
@Query("SELECT e FROM Expense e JOIN e.creditors c WHERE c.id = :personId AND e.isPaid = false")
|
// Find unpaid expenses where the creditor is a specific person
|
||||||
|
@Query("SELECT e FROM Expense e WHERE e.creditor.id = :personId AND e.isPaid = false")
|
||||||
Set<Expense> findUnpaidCreditorsExpensesByPersonId(@Param("personId") Long personId);
|
Set<Expense> findUnpaidCreditorsExpensesByPersonId(@Param("personId") Long personId);
|
||||||
|
|
||||||
@Query("SELECT e FROM Expense e JOIN e.debtors d WHERE d.id = :personId AND e.isPaid = false")
|
// Find unpaid expenses where the debtor is a specific person
|
||||||
|
@Query("SELECT e FROM Expense e WHERE e.debtor.id = :personId AND e.isPaid = false")
|
||||||
Set<Expense> findUnpaidDebtorsExpensesByPersonId(@Param("personId") Long personId);
|
Set<Expense> findUnpaidDebtorsExpensesByPersonId(@Param("personId") Long personId);
|
||||||
|
|
||||||
|
// Find expenses for a given year and filter by expense type and paid status
|
||||||
@Query("SELECT e FROM Expense e WHERE YEAR(e.date) = :year AND NOT (e.expenseType = :expenseType AND e.isPaid = true)")
|
@Query("SELECT e FROM Expense e WHERE YEAR(e.date) = :year AND NOT (e.expenseType = :expenseType AND e.isPaid = true)")
|
||||||
List<Expense> findByYearAndFilterCreditPaid(@Param("year") int year, @Param("expenseType") ExpenseType expenseType);
|
List<Expense> findByYearAndFilterCreditPaid(@Param("year") int year, @Param("expenseType") ExpenseType expenseType);
|
||||||
|
|
||||||
|
// Check if an expense with the given ID exists and is paid
|
||||||
boolean existsByIdAndIsPaidTrue(Long id);
|
boolean existsByIdAndIsPaidTrue(Long id);
|
||||||
|
|
||||||
List<Expense> findAllByOrderByDateDesc();}
|
// Find all expenses ordered by date descending
|
||||||
|
List<Expense> findAllByOrderByDateDesc();
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import javax.annotation.Nonnull;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@ -140,14 +141,21 @@ public class ExpenseService {
|
||||||
* @param expense the expense to set the type of
|
* @param expense the expense to set the type of
|
||||||
*/
|
*/
|
||||||
private void setExpenseType(final @Nonnull Expense expense) {
|
private void setExpenseType(final @Nonnull Expense expense) {
|
||||||
if (!expense.getCreditors().isEmpty())
|
// Check if the creditor is present
|
||||||
// If creditors are present, set type to CREDIT
|
if (Objects.nonNull(expense.getCreditor())) {
|
||||||
|
// If creditor is present, set type to CREDIT
|
||||||
expense.setExpenseType(ExpenseType.CREDIT);
|
expense.setExpenseType(ExpenseType.CREDIT);
|
||||||
else if (!expense.getDebtors().isEmpty())
|
}
|
||||||
// If debtors are present and no creditors, set type to DEBIT
|
// Check if the debtor is present and no creditor
|
||||||
|
else if (Objects.nonNull(expense.getDebtor())) {
|
||||||
|
// If debtor is present and no creditor, set type to DEBIT
|
||||||
expense.setExpenseType(ExpenseType.DEBIT);
|
expense.setExpenseType(ExpenseType.DEBIT);
|
||||||
else
|
}
|
||||||
// If neither creditors nor debtors are present, set type to NONE
|
// If neither creditor nor debtor is present
|
||||||
|
else {
|
||||||
|
// If neither creditor nor debtor is present, set type to NONE
|
||||||
expense.setExpenseType(ExpenseType.NONE);
|
expense.setExpenseType(ExpenseType.NONE);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import com.vaadin.flow.component.button.Button;
|
||||||
import com.vaadin.flow.component.button.ButtonVariant;
|
import com.vaadin.flow.component.button.ButtonVariant;
|
||||||
import com.vaadin.flow.component.checkbox.Checkbox;
|
import com.vaadin.flow.component.checkbox.Checkbox;
|
||||||
import com.vaadin.flow.component.combobox.ComboBox;
|
import com.vaadin.flow.component.combobox.ComboBox;
|
||||||
import com.vaadin.flow.component.combobox.MultiSelectComboBox;
|
|
||||||
import com.vaadin.flow.component.datepicker.DatePicker;
|
import com.vaadin.flow.component.datepicker.DatePicker;
|
||||||
import com.vaadin.flow.component.dependency.Uses;
|
import com.vaadin.flow.component.dependency.Uses;
|
||||||
import com.vaadin.flow.component.formlayout.FormLayout;
|
import com.vaadin.flow.component.formlayout.FormLayout;
|
||||||
|
@ -31,10 +30,8 @@ import jakarta.annotation.security.PermitAll;
|
||||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||||
import org.vaadin.klaudeta.PaginatedGrid;
|
import org.vaadin.klaudeta.PaginatedGrid;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@PermitAll
|
@PermitAll
|
||||||
@PageTitle("Expenses")
|
@PageTitle("Expenses")
|
||||||
|
@ -69,9 +66,10 @@ public class ExpensesView extends Div implements BeforeEnterObserver {
|
||||||
private ComboBox<PeriodUnit> periodUnit;
|
private ComboBox<PeriodUnit> periodUnit;
|
||||||
private TextField periodInterval;
|
private TextField periodInterval;
|
||||||
private DatePicker date;
|
private DatePicker date;
|
||||||
private MultiSelectComboBox<Person> creditors;
|
private ComboBox<Person> creditor;
|
||||||
private MultiSelectComboBox<Person> debtors;
|
private ComboBox<Person> debtor;
|
||||||
private ComboBox<Event> event;
|
private ComboBox<Event> event;
|
||||||
|
|
||||||
public ExpensesView(ExpenseService expenseService, CategoryService categoryService, PersonService personService, EventService eventService, ViewService viewService) {
|
public ExpensesView(ExpenseService expenseService, CategoryService categoryService, PersonService personService, EventService eventService, ViewService viewService) {
|
||||||
this.expenseService = expenseService;
|
this.expenseService = expenseService;
|
||||||
this.categoryService = categoryService;
|
this.categoryService = categoryService;
|
||||||
|
@ -102,7 +100,7 @@ public class ExpensesView extends Div implements BeforeEnterObserver {
|
||||||
|
|
||||||
grid.setItems(this.expenseService.findAllOrderByDateDescending());
|
grid.setItems(this.expenseService.findAllOrderByDateDescending());
|
||||||
grid.setPaginatorSize(5);
|
grid.setPaginatorSize(5);
|
||||||
grid.setPageSize(22); // setting page size
|
grid.setPageSize(22); // setting page size
|
||||||
grid.addThemeVariants(GridVariant.LUMO_NO_BORDER);
|
grid.addThemeVariants(GridVariant.LUMO_NO_BORDER);
|
||||||
|
|
||||||
// when a row is selected or deselected, populate form
|
// when a row is selected or deselected, populate form
|
||||||
|
@ -125,7 +123,7 @@ public class ExpensesView extends Div implements BeforeEnterObserver {
|
||||||
|
|
||||||
binder.forField(cost)
|
binder.forField(cost)
|
||||||
.asRequired("Cost is required")
|
.asRequired("Cost is required")
|
||||||
.withConverter( new StringToBigDecimalConverter("Invalid cost"))
|
.withConverter(new StringToBigDecimalConverter("Invalid cost"))
|
||||||
.bind(Expense::getCost, Expense::setCost);
|
.bind(Expense::getCost, Expense::setCost);
|
||||||
|
|
||||||
binder.forField(category)
|
binder.forField(category)
|
||||||
|
@ -155,21 +153,22 @@ public class ExpensesView extends Div implements BeforeEnterObserver {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Event listeners that will remove the selected creditors from the debtors list and vice versa
|
//TODO: THIS NEEDS TO BE IMPLEMENTED BUT FOR THE SINGLE PERSON NOW, STILL NEEDED
|
||||||
// Done so that the user cant create an expense with the same person as creditor and debtor
|
// // Event listeners that will remove the selected creditors from the debtors list and vice versa
|
||||||
debtors.addValueChangeListener(event -> {
|
// // Done so that the user cant create an expense with the same person as creditor and debtor
|
||||||
Set<Person> selectedDebtors = event.getValue();
|
// debtors.addValueChangeListener(event -> {
|
||||||
final var creditorsSet = new HashSet<>(personService.findAll());
|
// Set<Person> selectedDebtors = event.getValue();
|
||||||
creditorsSet.removeIf(selectedDebtors::contains);
|
// final var creditorsSet = new HashSet<>(personService.findAll());
|
||||||
creditors.setItems(creditorsSet);
|
// creditorsSet.removeIf(selectedDebtors::contains);
|
||||||
});
|
// creditors.setItems(creditorsSet);
|
||||||
|
// });
|
||||||
creditors.addValueChangeListener(event -> {
|
//
|
||||||
Set<Person> selectedCreditors = event.getValue();
|
// creditors.addValueChangeListener(event -> {
|
||||||
final var debtorsSet = new HashSet<>(personService.findAll());
|
// Set<Person> selectedCreditors = event.getValue();
|
||||||
debtorsSet.removeIf(selectedCreditors::contains);
|
// final var debtorsSet = new HashSet<>(personService.findAll());
|
||||||
debtors.setItems(debtorsSet);
|
// debtorsSet.removeIf(selectedCreditors::contains);
|
||||||
});
|
// debtors.setItems(debtorsSet);
|
||||||
|
// });
|
||||||
|
|
||||||
cancel.addClickListener(e -> {
|
cancel.addClickListener(e -> {
|
||||||
clearForm();
|
clearForm();
|
||||||
|
@ -205,7 +204,7 @@ public class ExpensesView extends Div implements BeforeEnterObserver {
|
||||||
UI.getCurrent().navigate(ExpensesView.class);
|
UI.getCurrent().navigate(ExpensesView.class);
|
||||||
} catch (ObjectOptimisticLockingFailureException exception) {
|
} catch (ObjectOptimisticLockingFailureException exception) {
|
||||||
Notification n = Notification.show(
|
Notification n = Notification.show(
|
||||||
"Error updating the data. Somebody else has updated the record while you were making changes.");
|
"Error deleting the data. Somebody else has updated the record while you were making changes.");
|
||||||
n.setPosition(Position.MIDDLE);
|
n.setPosition(Position.MIDDLE);
|
||||||
n.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
n.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||||
}
|
}
|
||||||
|
@ -218,7 +217,7 @@ public class ExpensesView extends Div implements BeforeEnterObserver {
|
||||||
if (expenseId.isPresent()) {
|
if (expenseId.isPresent()) {
|
||||||
Optional<Expense> expenseFromBackend = expenseService.get(expenseId.get());
|
Optional<Expense> expenseFromBackend = expenseService.get(expenseId.get());
|
||||||
if (expenseFromBackend.isPresent()) populateForm(expenseFromBackend.get());
|
if (expenseFromBackend.isPresent()) populateForm(expenseFromBackend.get());
|
||||||
else {
|
else {
|
||||||
Notification.show(
|
Notification.show(
|
||||||
String.format("The requested expense was not found, ID = %s", expenseId.get()), 3000,
|
String.format("The requested expense was not found, ID = %s", expenseId.get()), 3000,
|
||||||
Notification.Position.BOTTOM_START);
|
Notification.Position.BOTTOM_START);
|
||||||
|
@ -248,15 +247,15 @@ public class ExpensesView extends Div implements BeforeEnterObserver {
|
||||||
periodUnit = new ComboBox<>("Period Unit");
|
periodUnit = new ComboBox<>("Period Unit");
|
||||||
periodUnit.setItems(PeriodUnit.values());
|
periodUnit.setItems(PeriodUnit.values());
|
||||||
periodInterval = new TextField("Period Interval");
|
periodInterval = new TextField("Period Interval");
|
||||||
creditors = new MultiSelectComboBox<>("Creditors");
|
creditor = new ComboBox<>("Creditor");
|
||||||
creditors.setItems(personService.findAll());
|
creditor.setItems(personService.findAll());
|
||||||
creditors.setItemLabelGenerator(Person::getFirstName);
|
creditor.setItemLabelGenerator(Person::getFirstName);
|
||||||
event = new ComboBox<>("Event");
|
event = new ComboBox<>("Event");
|
||||||
event.setItems(eventService.findAll());
|
event.setItems(eventService.findAll());
|
||||||
event.setItemLabelGenerator(Event::getName);
|
event.setItemLabelGenerator(Event::getName);
|
||||||
debtors = new MultiSelectComboBox<>("Debtors");
|
debtor = new ComboBox<>("Debtor");
|
||||||
debtors.setItems(personService.findAll());
|
debtor.setItems(personService.findAll());
|
||||||
debtors.setItemLabelGenerator(Person::getFirstName);
|
debtor.setItemLabelGenerator(Person::getFirstName);
|
||||||
date = new DatePicker("Date");
|
date = new DatePicker("Date");
|
||||||
|
|
||||||
// Horizontal layout for checkboxes
|
// Horizontal layout for checkboxes
|
||||||
|
@ -265,7 +264,7 @@ public class ExpensesView extends Div implements BeforeEnterObserver {
|
||||||
isPaid = new Checkbox("Paid");
|
isPaid = new Checkbox("Paid");
|
||||||
checkboxLayout.add(isPeriodic, isPaid);
|
checkboxLayout.add(isPeriodic, isPaid);
|
||||||
|
|
||||||
formLayout.add(name, cost, category, description, checkboxLayout, periodUnit, periodInterval, date, creditors, debtors, event);
|
formLayout.add(name, cost, category, description, checkboxLayout, periodUnit, periodInterval, date, creditor, debtor, event);
|
||||||
editorDiv.add(formLayout);
|
editorDiv.add(formLayout);
|
||||||
createButtonLayout(editorLayoutDiv);
|
createButtonLayout(editorLayoutDiv);
|
||||||
|
|
||||||
|
@ -305,5 +304,9 @@ public class ExpensesView extends Div implements BeforeEnterObserver {
|
||||||
boolean isPeriodicChecked = (value != null) && value.getIsPeriodic();
|
boolean isPeriodicChecked = (value != null) && value.getIsPeriodic();
|
||||||
periodUnit.setVisible(isPeriodicChecked);
|
periodUnit.setVisible(isPeriodicChecked);
|
||||||
periodInterval.setVisible(isPeriodicChecked);
|
periodInterval.setVisible(isPeriodicChecked);
|
||||||
|
|
||||||
|
// Set selected items for creditor and debtor
|
||||||
|
creditor.setValue(value != null ? value.getCreditor() : null);
|
||||||
|
debtor.setValue(value != null ? value.getDebtor() : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue