Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.SkillsForge.expensetracker.app.filter;

import com.SkillsForge.expensetracker.app.enums.TransactionCategory;
import com.SkillsForge.expensetracker.app.enums.TransactionType;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class TransactionFilter {
private TransactionCategory category;
private TransactionType type;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@

import com.SkillsForge.expensetracker.dto.CreateTransactionRequest;
import com.SkillsForge.expensetracker.dto.TransactionDto;
import com.SkillsForge.expensetracker.app.filter.TransactionFilter;
import com.SkillsForge.expensetracker.dto.TransactionUpdateRequest;
import com.SkillsForge.expensetracker.service.TransactionService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/api/v1/transaction")
@RequestMapping("/api/v1/transactions")
public class TransactionController {
private final TransactionService transactionService;

Expand All @@ -29,4 +33,20 @@ public ResponseEntity<TransactionDto> getTransactionById(@PathVariable Long id)
TransactionDto response = transactionService.getTransactionById(id);
return ResponseEntity.ok(response);
}

@PutMapping("/{id}")
public TransactionDto updateTransaction(
@PathVariable Long id,
@RequestBody TransactionUpdateRequest request
) {
return transactionService.updateTransaction(id, request);
}

@GetMapping
public Page<TransactionDto> getAllTransactions(
@ModelAttribute TransactionFilter filter,
Pageable pageable
) {
return transactionService.getAllTransactions(filter, pageable);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.SkillsForge.expensetracker.dto;

import com.SkillsForge.expensetracker.app.enums.TransactionCategory;
import com.SkillsForge.expensetracker.app.enums.TransactionType;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDate;

@Getter
@Setter
public class TransactionUpdateRequest {

private String description;
private TransactionCategory category;
private TransactionType type;
private Long amount;
private LocalDate date;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@

import com.SkillsForge.expensetracker.persistence.entity.Transaction;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface TransactionRepository extends JpaRepository<Transaction, Long> {}
public interface TransactionRepository
extends JpaRepository<Transaction, Long>,
JpaSpecificationExecutor<Transaction> {
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
package com.SkillsForge.expensetracker.service;

import com.SkillsForge.expensetracker.dto.CreateTransactionRequest;
import com.SkillsForge.expensetracker.dto.TransactionDto;
import com.SkillsForge.expensetracker.dto.TransactionUpdateRequest;
import com.SkillsForge.expensetracker.app.filter.TransactionFilter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import com.SkillsForge.expensetracker.dto.CreateTransactionRequest;

public interface TransactionService {
TransactionDto createTransaction(CreateTransactionRequest request);

TransactionDto getTransactionById(Long id);

TransactionDto updateTransaction(Long id, TransactionUpdateRequest request);

Page<TransactionDto> getAllTransactions(TransactionFilter filter, Pageable pageable);


}
Original file line number Diff line number Diff line change
@@ -1,49 +1,104 @@
package com.SkillsForge.expensetracker.service;

import com.SkillsForge.expensetracker.app.filter.TransactionFilter;
import com.SkillsForge.expensetracker.dto.CreateTransactionRequest;
import com.SkillsForge.expensetracker.dto.TransactionDto;
import com.SkillsForge.expensetracker.dto.TransactionUpdateRequest;
import com.SkillsForge.expensetracker.exception.ResourceNotFoundException;
import com.SkillsForge.expensetracker.persistence.entity.Transaction;
import com.SkillsForge.expensetracker.persistence.repository.TransactionRepository;
import java.time.LocalDateTime;
import jakarta.persistence.criteria.Predicate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
@RequiredArgsConstructor
public class TransactionServiceImpl implements TransactionService {

private final TransactionRepository transactionRepository;

@Override
@Transactional
public TransactionDto createTransaction(CreateTransactionRequest request) {
LocalDateTime now = LocalDateTime.now();

Transaction transaction = new Transaction(request);
transaction.setCreatedAt(now);
transaction.setUpdatedAt(now);

Transaction savedTransaction = transactionRepository.save(transaction);
log.info("Transaction created successfully with ID: {}", savedTransaction.getId());
return TransactionDto.fromEntity(savedTransaction);
}

@Override
@Transactional(readOnly = true)
public TransactionDto getTransactionById(Long id) {
log.info("Fetching transaction with ID: {}", id);

return TransactionDto.fromEntity(
transactionRepository
.findById(id)
.orElseThrow(
() -> {
log.error("Transaction not found with ID: {}", id);
return new ResourceNotFoundException("Transaction not found with ID: " + id);
}));
}
private final TransactionRepository transactionRepository;

// ================= CREATE =================
@Override
@Transactional
public TransactionDto createTransaction(CreateTransactionRequest request) {
LocalDateTime now = LocalDateTime.now();

Transaction transaction = new Transaction(request);
transaction.setCreatedAt(now);
transaction.setUpdatedAt(now);

Transaction saved = transactionRepository.save(transaction);
log.info("Transaction created successfully with ID: {}", saved.getId());

return TransactionDto.fromEntity(saved);
}

// ================= GET BY ID =================
@Override
@Transactional(readOnly = true)
public TransactionDto getTransactionById(Long id) {
log.info("Fetching transaction with ID: {}", id);

Transaction transaction = transactionRepository.findById(id)
.orElseThrow(() ->
new ResourceNotFoundException("Transaction not found with ID: " + id));

return TransactionDto.fromEntity(transaction);
}

// ================= UPDATE =================
@Override
@Transactional
public TransactionDto updateTransaction(Long id, TransactionUpdateRequest request) {

Transaction existing = transactionRepository.findById(id)
.orElseThrow(() ->
new ResourceNotFoundException("Transaction not found with ID: " + id));

existing.setDescription(request.getDescription());
existing.setCategory(request.getCategory());
existing.setType(request.getType());
existing.setAmount(request.getAmount());
existing.setDate(request.getDate());
existing.setUpdatedAt(LocalDateTime.now());

Transaction saved = transactionRepository.save(existing);
log.info("Transaction updated successfully with ID: {}", saved.getId());

return TransactionDto.fromEntity(saved);
}

// ================= GET ALL (FILTERED) =================
@Override
Comment thread
adelekeemmanuel marked this conversation as resolved.
@Transactional(readOnly = true)
public Page<TransactionDto> getAllTransactions(TransactionFilter filter, Pageable pageable) {

Specification<Transaction> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();

if (filter.getCategory() != null) {
predicates.add(cb.equal(root.get("category"), filter.getCategory()));
}

if (filter.getType() != null) {
predicates.add(cb.equal(root.get("type"), filter.getType()));
}

return cb.and(predicates.toArray(new Predicate[0]));
};

return transactionRepository
.findAll(spec, pageable)
.map(TransactionDto::fromEntity);
}
}