feat: history entity, repository and Expense.lastModified

This commit is contained in:
2026-07-06 12:21:05 +02:00
parent 6203a83af3
commit 9d1cf862c7
6 changed files with 88 additions and 0 deletions
@@ -1,6 +1,7 @@
package de.zendric.app.xpensely_server.model; package de.zendric.app.xpensely_server.model;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonBackReference;
@@ -36,6 +37,7 @@ public class Expense {
private Double otherPersonAmount; private Double otherPersonAmount;
private String category; private String category;
private LocalDate date; private LocalDate date;
private LocalDateTime lastModified;
@ManyToOne @ManyToOne
@JoinColumn(name = "expense_list_id", nullable = false) @JoinColumn(name = "expense_list_id", nullable = false)
@@ -0,0 +1,52 @@
package de.zendric.app.xpensely_server.model;
import java.time.LocalDateTime;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "expense_history_entry", indexes = @Index(name = "idx_history_list_timestamp", columnList = "expense_list_id, timestamp DESC"))
public class ExpenseHistoryEntry {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "expense_list_id", nullable = false)
private ExpenseList expenseList;
/** Plain column on purpose — no FK, so history survives expense deletion. */
private Long expenseId;
/** Denormalized so entries for deleted expenses still render. */
private String expenseTitle;
@ManyToOne
private AppUser actor;
@Enumerated(EnumType.STRING)
private HistoryEntryType type;
private LocalDateTime timestamp;
/** JSON array of FieldChange; null for CREATED/DELETED. */
@Column(columnDefinition = "TEXT")
private String changes;
}
@@ -0,0 +1,9 @@
package de.zendric.app.xpensely_server.model;
import com.fasterxml.jackson.annotation.JsonProperty;
public record FieldChange(
String field,
@JsonProperty("old") String oldValue,
@JsonProperty("new") String newValue) {
}
@@ -0,0 +1,5 @@
package de.zendric.app.xpensely_server.model;
public enum HistoryEntryType {
CREATED, UPDATED, DELETED
}
@@ -0,0 +1,18 @@
package de.zendric.app.xpensely_server.repo;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import de.zendric.app.xpensely_server.model.ExpenseHistoryEntry;
public interface ExpenseHistoryRepository extends JpaRepository<ExpenseHistoryEntry, Long> {
Page<ExpenseHistoryEntry> findByExpenseListIdOrderByTimestampDescIdDesc(Long expenseListId, Pageable pageable);
@Query("select h.expenseId from ExpenseHistoryEntry h")
List<Long> findAllExpenseIds();
}
@@ -10,4 +10,6 @@ import de.zendric.app.xpensely_server.model.Expense;
@Repository @Repository
public interface ExpenseRepository extends JpaRepository<Expense, Long> { public interface ExpenseRepository extends JpaRepository<Expense, Long> {
List<Expense> findAllByOrderByDateAsc(); List<Expense> findAllByOrderByDateAsc();
List<Expense> findByLastModifiedIsNull();
} }