<repo_name>fabricio22/api-controle-gasto<file_sep>/src/main/java/br/com/controle/gastos/vo/MovimentoAtualizacaoVo.java
package br.com.controle.gastos.vo;

import java.math.BigDecimal;
import java.util.Date;

import br.com.controle.gastos.enums.TipoMovimento;
import br.com.controle.gastos.models.Categoria;

public class MovimentoAtualizacaoVo {

	private Date dataReferencia;
	private String descricaoMovimento;
	private BigDecimal valorMovimento;
	private TipoMovimento tipoMovimento;
	private Categoria categoria;

	public MovimentoAtualizacaoVo() {

	}

	public Date getDataReferencia() {
		return dataReferencia;
	}

	public void setDataReferencia(Date dataReferencia) {
		this.dataReferencia = dataReferencia;
	}

	public String getDescricaoMovimento() {
		return descricaoMovimento;
	}

	public void setDescricaoMovimento(String descricaoMovimento) {
		this.descricaoMovimento = descricaoMovimento;
	}

	public BigDecimal getValorMovimento() {
		return valorMovimento;
	}

	public void setValorMovimento(BigDecimal valorMovimento) {
		this.valorMovimento = valorMovimento;
	}

	public TipoMovimento getTipoMovimento() {
		return tipoMovimento;
	}

	public void setTipoMovimento(TipoMovimento tipoMovimento) {
		this.tipoMovimento = tipoMovimento;
	}

	public Categoria getCategoria() {
		return categoria;
	}

	public void setCategoria(Categoria categoria) {
		this.categoria = categoria;
	}
}
<file_sep>/src/main/java/br/com/controle/gastos/vo/CategoriaDescricaoVo.java
package br.com.controle.gastos.vo;

import java.util.Date;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;

import br.com.controle.gastos.models.Categoria;

public class CategoriaDescricaoVo {

	@NotBlank(message = "Não é permitido categoria em branco")
	@NotEmpty(message = "Não é permitido categoria vazia.")
	private String descricao;

	public CategoriaDescricaoVo() {

	}

	public String getDescricao() {
		return descricao;
	}

	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}
	
	public static Categoria converterCategoria(String descricao) {
		Categoria categoria = new Categoria();
		categoria.setDescricao(descricao);
		categoria.setDataInclusao(new Date());
		
		return categoria;
	}
}
<file_sep>/src/main/java/br/com/controle/gastos/services/CategoriaServiceImpl.java
package br.com.controle.gastos.services;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import br.com.controle.gastos.models.Categoria;
import br.com.controle.gastos.repository.CategoriaRepository;
import br.com.controle.gastos.vo.CategoriaDescricaoVo;
import br.com.controle.gastos.vo.CategoriaVo;

@Service
public class CategoriaServiceImpl implements CategoriaService {

	@Autowired
	CategoriaRepository categoriaRepository;

	@Override
	public CategoriaVo retornaCategoria(Long id) {
		return categoriaRepository.findById(id).map(categoria -> new CategoriaVo(categoria)).get();
	}

	@Override
	public CategoriaVo criarCategoria(CategoriaDescricaoVo categoriaDesricaoVo) {
		Categoria categoria = categoriaRepository
				.save(CategoriaDescricaoVo.converterCategoria(categoriaDesricaoVo.getDescricao()));
		return new CategoriaVo(categoria);
	}

	@Override
	public List<CategoriaVo> listaCategorias() {
		return categoriaRepository.findAll().stream().map(categoria -> new CategoriaVo(categoria))
				.collect(Collectors.toList());
	}

	@Override
	public void deleteCategoria(Long id) {
		categoriaRepository.deleteById(id);
	}

	@Override
	public CategoriaVo atualizaCategoria(Long id, CategoriaDescricaoVo descricao) {
		Optional<Categoria> optionalCategoria = categoriaRepository.findById(id);

		if (optionalCategoria.isPresent()) {
			Categoria categoria = optionalCategoria.get();
			categoria.setDescricao(descricao.getDescricao());
			categoriaRepository.save(categoria);
			return new CategoriaVo(categoria);
		}

		return null;
	}

}
<file_sep>/src/main/java/br/com/controle/gastos/models/SumarizacaoDia.java
package br.com.controle.gastos.models;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import br.com.controle.gastos.vo.SumarizacaoVo;

@Entity
@Table(name = "SUMARIZACAODIA")
public class SumarizacaoDia {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "ID_SUMARIZACAO_DIA")
	private Long id;
	@Column(name = "DATA")
	private Date data;
	@Column(name = "VALOR_TOTAL")
	private BigDecimal valorTotal;
	@Column(name = "DATA_INCLUSAO")
	private Date dataInclusao;

	public SumarizacaoDia() {

	}

	public SumarizacaoDia(SumarizacaoVo sumarizacaoVo) {
		this.data = sumarizacaoVo.getData();
		this.valorTotal = sumarizacaoVo.getValorTotal();
		this.dataInclusao = new Date();
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public Date getData() {
		return data;
	}

	public void setData(Date data) {
		this.data = data;
	}

	public BigDecimal getValorTotal() {
		return valorTotal;
	}

	public void setValorTotal(BigDecimal valorTotal) {
		this.valorTotal = valorTotal;
	}

	public Date getDataInclusao() {
		return dataInclusao;
	}

	public void setDataInclusao(Date dataInclusao) {
		this.dataInclusao = dataInclusao;
	}
}
<file_sep>/src/main/java/br/com/controle/gastos/vo/CategoriaVo.java
package br.com.controle.gastos.vo;

import java.util.Date;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;

import com.fasterxml.jackson.annotation.JsonFormat;

import br.com.controle.gastos.models.Categoria;

public class CategoriaVo {

	private Long id;
	@NotBlank(message = "Não é permitido categoria em branco")
	@NotEmpty(message = "Não é permitido categoria vazia.")
	private String descricao;
	@JsonFormat(pattern = "dd/MM/yyyy")
	private Date dataInclusao;

	public CategoriaVo() {

	}

	public CategoriaVo(Categoria categoria) {
		this.id = categoria.getId();
		this.descricao = categoria.getDescricao();
		this.dataInclusao = categoria.getDataInclusao();
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getDescricao() {
		return descricao;
	}

	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}

	public Date getDataInclusao() {
		return dataInclusao;
	}

	public void setDataInclusao(Date dataInclusao) {
		this.dataInclusao = dataInclusao;
	}
}
<file_sep>/src/main/java/br/com/controle/gastos/vo/SumarizacaoVo.java
package br.com.controle.gastos.vo;

import java.math.BigDecimal;
import java.util.Date;

public class SumarizacaoVo {

	private Date data;
	private BigDecimal valorTotal;

	public SumarizacaoVo() {

	}
	
	public SumarizacaoVo(Date data, BigDecimal valorTotal) {
		this.data = data;
	    this.valorTotal = valorTotal;				
	}

	public Date getData() {
		return data;
	}

	public void setData(Date data) {
		this.data = data;
	}

	public BigDecimal getValorTotal() {
		return valorTotal;
	}

	public void setValorTotal(BigDecimal valorTotal) {
		this.valorTotal = valorTotal;
	}
}
<file_sep>/src/main/java/br/com/controle/gastos/repository/SumarizacaoDiaRepository.java
package br.com.controle.gastos.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import br.com.controle.gastos.models.SumarizacaoDia;
import br.com.controle.gastos.vo.SumarizacaoGeralVo;
import br.com.controle.gastos.vo.SumarizacaoVo;

@Repository
public interface SumarizacaoDiaRepository extends JpaRepository<SumarizacaoDia, Long> {

	@Query(value = "SELECT new br.com.controle.gastos.vo.SumarizacaoVo(m.dataReferencia AS data,"
			+ " SUM(m.valorMovimento) AS valorTotal)" + " FROM Movimentacao m" + " GROUP BY m.dataReferencia")
	public List<SumarizacaoVo> getSumarizacaoEntradaDia();

	@Query(value = "SELECT new br.com.controle.gastos.vo.SumarizacaoGeralVo(SUM(m.valorMovimento) AS valorTotal) FROM Movimentacao m")
	public SumarizacaoGeralVo getTotal();

}
<file_sep>/src/main/java/br/com/controle/gastos/models/Movimentacao.java
package br.com.controle.gastos.models;

import java.math.BigDecimal;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import br.com.controle.gastos.enums.TipoMovimento;

@Entity
@Table(name = "MOVIMENTACOES")
public class Movimentacao {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "ID_MOVIMENTACOES")
	private Long id;
	@Column(name = "DATA_REFERENCIA")
	private Date dataReferencia;
	@Column(name = "DESCRICAO_MOVIMENTO")
	private String descricaoMovimento;
	@Column(name = "VALOR_MOVIMENTO")
	private BigDecimal valorMovimento;
	@Column(name = "DATA_INCLUSAO")
	private Date dataInclusao;
	@Column(name = "TIPO_MOVIMENTO")
	@Enumerated(EnumType.STRING)
	private TipoMovimento tipoMovimento;
	@ManyToOne
	@JoinColumn(name = "ID_CATEGORIA")
	private Categoria categoria;

	public Movimentacao() {
		this.dataInclusao = new Date();
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public Date getDataReferencia() {
		return dataReferencia;
	}

	public void setDataReferencia(Date dataReferencia) {
		this.dataReferencia = dataReferencia;
	}

	public String getDescricaoMovimento() {
		return descricaoMovimento;
	}

	public void setDescricaoMovimento(String descricaoMovimento) {
		this.descricaoMovimento = descricaoMovimento;
	}

	public BigDecimal getValorMovimento() {
		return valorMovimento;
	}

	public void setValorMovimento(BigDecimal valorMovimento) {
		this.valorMovimento = valorMovimento;
	}

	public Date getDataInclusao() {
		return dataInclusao;
	}

	public void setDataInclusao(Date dataInclusao) {
		this.dataInclusao = dataInclusao;
	}

	public TipoMovimento getTipoMovimento() {
		return tipoMovimento;
	}

	public void setTipoMovimento(TipoMovimento tipoMovimento) {
		this.tipoMovimento = tipoMovimento;
	}

	public Categoria getCategoria() {
		return categoria;
	}

	public void setCategoria(Categoria categoria) {
		this.categoria = categoria;
	}

}
<file_sep>/src/main/java/br/com/controle/gastos/controller/CategoriaController.java
package br.com.controle.gastos.controller;

import java.util.List;
import java.util.stream.Collectors;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import br.com.controle.gastos.services.CategoriaService;
import br.com.controle.gastos.vo.CategoriaDescricaoVo;
import br.com.controle.gastos.vo.CategoriaVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

@Api(value = "Api Rest - Controle de Gastos - Módulo Categoria")
@RestController
@RequestMapping("/categoria")
public class CategoriaController {

	@Autowired
	CategoriaService categoriaService;

	@ApiOperation(value = "Retorna uma categoria por Id.")
	@GetMapping("/{id}")
	@ResponseBody
	@ResponseStatus(code = HttpStatus.OK)
	public ResponseEntity<?> retornaCategoria(@PathVariable("id") Long id) {
		CategoriaVo categoriaRetornada = categoriaService.retornaCategoria(id);
		return new ResponseEntity<CategoriaVo>(categoriaRetornada, HttpStatus.OK);
	}

	@ApiOperation(value = "Cria uma categoria")
	@PostMapping
	@ResponseBody
	@ResponseStatus(code = HttpStatus.CREATED)
	public ResponseEntity<?> criarCategoria(@Valid @RequestBody CategoriaDescricaoVo categoriaDescricao,
			Errors errors) {
		if (!errors.hasErrors()) {
			return new ResponseEntity<CategoriaVo>(categoriaService.criarCategoria(categoriaDescricao),
					HttpStatus.CREATED);
		}

		return ResponseEntity.badRequest().body(errors.getAllErrors().stream()
				.map(mensagem -> mensagem.getDefaultMessage()).collect(Collectors.joining(",")));
	}

	@ApiOperation(value = "Retorna uma lista de categoria")
	@GetMapping("/lista-categoria")
	@ResponseStatus(code = HttpStatus.OK)
	@ResponseBody
	public ResponseEntity<?> listaCategorias() {
		return new ResponseEntity<List>(categoriaService.listaCategorias(), HttpStatus.OK);
	}

	@ApiOperation(value = "Remove uma categoria")
	@DeleteMapping("/{id}")
	@ResponseStatus(code = HttpStatus.NO_CONTENT)
	public void deleteCategoria(@PathVariable("id") Long id) {
		categoriaService.deleteCategoria(id);
	}

	@ApiOperation(value = "Atualiza Categoria")
	@PatchMapping("/{id}")
	@ResponseBody
	@ResponseStatus(code = HttpStatus.OK)
	public ResponseEntity<?> atualizaCategoria(@Valid @PathVariable("id") Long id,
			@RequestBody CategoriaDescricaoVo descricao, Errors errors) {

		if (!errors.hasErrors()) {
			return new ResponseEntity<CategoriaVo>(categoriaService.atualizaCategoria(id, descricao), HttpStatus.OK);
		}

		return ResponseEntity.badRequest().body(errors.getAllErrors().stream()
				.map(mensagem -> mensagem.getDefaultMessage()).collect(Collectors.joining(",")));
	}

}
<file_sep>/src/main/java/br/com/controle/gastos/services/MovimentacoesServiceImpl.java
package br.com.controle.gastos.services;

import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import br.com.controle.gastos.enums.TipoMovimento;
import br.com.controle.gastos.models.Movimentacao;
import br.com.controle.gastos.models.SumarizacaoDia;
import br.com.controle.gastos.repository.MovimentacoesRepository;
import br.com.controle.gastos.repository.SumarizacaoDiaRepository;
import br.com.controle.gastos.vo.MovimentacaoVo;
import br.com.controle.gastos.vo.MovimentoAtualizacaoVo;
import br.com.controle.gastos.vo.SumarizacaoGeralVo;

@Service
public class MovimentacoesServiceImpl implements MovimentacoesService {

	@Autowired
	MovimentacoesRepository movimentacoesRepository;

	@Autowired
	SumarizacaoDiaRepository sumarizacaoDiaRepository;

	@Override
	public MovimentacaoVo criarMovimento(Movimentacao movimentacao) {

		if (movimentacao.getTipoMovimento() == TipoMovimento.SAIDA) {
			movimentacao.setValorMovimento(BigDecimal.valueOf(movimentacao.getValorMovimento().doubleValue() * -1));
		}

		if (movimentacao.getTipoMovimento() == TipoMovimento.ENTRADA) {
			movimentacao
					.setValorMovimento(BigDecimal.valueOf(Math.abs(movimentacao.getValorMovimento().doubleValue())));
		}

		this.movimentacoesRepository.save(movimentacao);
		atualizaSumarizacaoDia();

		return new MovimentacaoVo(movimentacao);

	}

	private void atualizaSumarizacaoDia() {
		this.sumarizacaoDiaRepository.deleteAll();
		this.sumarizacaoDiaRepository.getSumarizacaoEntradaDia().forEach(sumarizacao -> {
			this.sumarizacaoDiaRepository.save(new SumarizacaoDia(sumarizacao));
		});
	}

	@Override
	public List<MovimentacaoVo> listaMovimentos() {
		return this.movimentacoesRepository.findAll().stream().map(movimentacao -> new MovimentacaoVo(movimentacao))
				.collect(Collectors.toList());
	}

	@Override
	public void removeMovimento(Long id) {
		this.movimentacoesRepository.deleteById(id);
		atualizaSumarizacaoDia();
	}

	@Override
	public MovimentacaoVo atualizaMovimento(Long id, MovimentoAtualizacaoVo movimentos) {
		return this.movimentacoesRepository.findById(id).map(movimento -> {
			movimento.setCategoria(movimentos.getCategoria());
			movimento.setDataReferencia(movimentos.getDataReferencia());
			movimento.setDescricaoMovimento(movimentos.getDescricaoMovimento());
			movimento.setTipoMovimento(movimentos.getTipoMovimento());

			if ((TipoMovimento.SAIDA == movimentos.getTipoMovimento()
					&& movimentos.getValorMovimento().doubleValue() > 0)
					|| (TipoMovimento.ENTRADA == movimentos.getTipoMovimento()
							&& movimentos.getValorMovimento().doubleValue() < 0)) {

				throw new RuntimeException("Valor do movimento é inválido para o tipo de movimento.");
			}

			if (movimento.getValorMovimento() != movimentos.getValorMovimento()) {
				movimento.setValorMovimento(movimentos.getValorMovimento());

				this.movimentacoesRepository.save(movimento);
				atualizaSumarizacaoDia();

			} else {
				movimento.setValorMovimento(movimentos.getValorMovimento());
				this.movimentacoesRepository.save(movimento);
			}

			return new MovimentacaoVo(movimento);

		}).get();

	}

	@Override
	public SumarizacaoGeralVo getTotalGeral() {
		return this.sumarizacaoDiaRepository.getTotal();
	}

}
<file_sep>/src/main/java/br/com/controle/gastos/models/Categoria.java
package br.com.controle.gastos.models;

import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import com.mysql.cj.x.protobuf.MysqlxCursor.Fetch;

@Entity
@Table(name = "CATEGORIAS")
public class Categoria {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "ID_CATEGORIAS")
	private Long id;

	@Column(name = "DESCRICAO")
	private String descricao;

	@Column(name = "DATA_INCLUSAO")
	private Date dataInclusao;

	@OneToMany(mappedBy = "categoria", targetEntity = Movimentacao.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
	private List<Movimentacao> movimentacao;

	public Categoria() {

	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getDescricao() {
		return descricao;
	}

	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}

	public Date getDataInclusao() {
		return dataInclusao;
	}

	public void setDataInclusao(Date dataInclusao) {
		this.dataInclusao = dataInclusao;
	}
}
<file_sep>/src/main/java/br/com/controle/gastos/repository/MovimentacoesRepository.java
package br.com.controle.gastos.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import br.com.controle.gastos.models.Movimentacao;

public interface MovimentacoesRepository extends JpaRepository<Movimentacao, Long> {

}
<file_sep>/src/main/java/br/com/controle/gastos/services/CategoriaService.java
package br.com.controle.gastos.services;

import java.util.List;

import br.com.controle.gastos.vo.CategoriaDescricaoVo;
import br.com.controle.gastos.vo.CategoriaVo;

public interface CategoriaService {
	
	public CategoriaVo retornaCategoria(Long id);
	
	public CategoriaVo criarCategoria(CategoriaDescricaoVo categoriaDesricaoVo);
	
	public List<CategoriaVo> listaCategorias();
	
	public void deleteCategoria(Long id);
	
	public CategoriaVo atualizaCategoria(Long id, CategoriaDescricaoVo descricao);

}
