diff --git a/README.md b/README.md index 699fed9..3339e96 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,14 @@ Os status disponíveis são `ABERTA`, `EM_ANDAMENTO`, `CONCLUIDA` e } ``` +Transições permitidas: + +- `ABERTA` → `EM_ANDAMENTO` ou `CANCELADA`; +- `EM_ANDAMENTO` → `CONCLUIDA` ou `CANCELADA`; +- `CONCLUIDA` e `CANCELADA` são estados finais. + +Uma transição não permitida retorna `409 Conflict`. + Cada ordem também informa `criadaEm` e `atualizadaEm` em UTC. Esses campos são preenchidos automaticamente e não precisam ser enviados nas requisições. diff --git a/src/main/java/com/exemplo/ordem_servico/exception/ApiExceptionHandler.java b/src/main/java/com/exemplo/ordem_servico/exception/ApiExceptionHandler.java index da59040..40bdac4 100644 --- a/src/main/java/com/exemplo/ordem_servico/exception/ApiExceptionHandler.java +++ b/src/main/java/com/exemplo/ordem_servico/exception/ApiExceptionHandler.java @@ -44,4 +44,14 @@ public ProblemDetail tratarValidacao( problema.setProperty("campos", campos); return problema; } + + @ExceptionHandler(TransicaoStatusInvalidaException.class) + public ProblemDetail tratarTransicaoStatusInvalida( + TransicaoStatusInvalidaException exception) { + ProblemDetail problema = ProblemDetail.forStatusAndDetail( + HttpStatus.CONFLICT, + exception.getMessage()); + problema.setTitle("Transição de status inválida"); + return problema; + } } diff --git a/src/main/java/com/exemplo/ordem_servico/exception/TransicaoStatusInvalidaException.java b/src/main/java/com/exemplo/ordem_servico/exception/TransicaoStatusInvalidaException.java new file mode 100644 index 0000000..5e33067 --- /dev/null +++ b/src/main/java/com/exemplo/ordem_servico/exception/TransicaoStatusInvalidaException.java @@ -0,0 +1,15 @@ +package com.exemplo.ordem_servico.exception; + +import com.exemplo.ordem_servico.model.StatusOrdemServico; + +public class TransicaoStatusInvalidaException extends RuntimeException { + + public TransicaoStatusInvalidaException( + StatusOrdemServico statusAtual, + StatusOrdemServico novoStatus) { + super("Não é permitido alterar o status de " + + statusAtual + + " para " + + novoStatus); + } +} diff --git a/src/main/java/com/exemplo/ordem_servico/model/StatusOrdemServico.java b/src/main/java/com/exemplo/ordem_servico/model/StatusOrdemServico.java index 4993491..62514b5 100644 --- a/src/main/java/com/exemplo/ordem_servico/model/StatusOrdemServico.java +++ b/src/main/java/com/exemplo/ordem_servico/model/StatusOrdemServico.java @@ -4,5 +4,19 @@ public enum StatusOrdemServico { ABERTA, EM_ANDAMENTO, CONCLUIDA, - CANCELADA + CANCELADA; + + public boolean podeTransicionarPara(StatusOrdemServico proximoStatus) { + if (this == proximoStatus) { + return true; + } + + return switch (this) { + case ABERTA -> proximoStatus == EM_ANDAMENTO + || proximoStatus == CANCELADA; + case EM_ANDAMENTO -> proximoStatus == CONCLUIDA + || proximoStatus == CANCELADA; + case CONCLUIDA, CANCELADA -> false; + }; + } } diff --git a/src/main/java/com/exemplo/ordem_servico/service/OrdemServicoService.java b/src/main/java/com/exemplo/ordem_servico/service/OrdemServicoService.java index dc50964..ba06e39 100644 --- a/src/main/java/com/exemplo/ordem_servico/service/OrdemServicoService.java +++ b/src/main/java/com/exemplo/ordem_servico/service/OrdemServicoService.java @@ -5,6 +5,7 @@ import org.springframework.stereotype.Service; import com.exemplo.ordem_servico.exception.OrdemServicoNaoEncontradaException; +import com.exemplo.ordem_servico.exception.TransicaoStatusInvalidaException; import com.exemplo.ordem_servico.model.OrdemServico; import com.exemplo.ordem_servico.model.StatusOrdemServico; import com.exemplo.ordem_servico.repository.OrdemServicoRepository; @@ -53,6 +54,13 @@ public OrdemServico atualizar(Long id, OrdemServico novosDados) { public OrdemServico atualizarStatus(Long id, StatusOrdemServico status) { OrdemServico ordem = buscarPorId(id); + StatusOrdemServico statusAtual = ordem.getStatus(); + if (!statusAtual.podeTransicionarPara(status)) { + throw new TransicaoStatusInvalidaException(statusAtual, status); + } + if (statusAtual == status) { + return ordem; + } ordem.setStatus(status); return repository.save(ordem); } diff --git a/src/test/java/com/exemplo/ordem_servico/exception/ApiExceptionHandlerTests.java b/src/test/java/com/exemplo/ordem_servico/exception/ApiExceptionHandlerTests.java index 73b0371..a6baf61 100644 --- a/src/test/java/com/exemplo/ordem_servico/exception/ApiExceptionHandlerTests.java +++ b/src/test/java/com/exemplo/ordem_servico/exception/ApiExceptionHandlerTests.java @@ -11,6 +11,8 @@ import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; +import com.exemplo.ordem_servico.model.StatusOrdemServico; + class ApiExceptionHandlerTests { private final ApiExceptionHandler handler = new ApiExceptionHandler(); @@ -48,4 +50,18 @@ void deveInformarCamposComErroDeValidacao() { Map.of("cliente", "O nome do cliente é obrigatório!"), problema.getProperties().get("campos")); } + + @Test + void devePadronizarErroDeTransicaoDeStatus() { + ProblemDetail problema = handler.tratarTransicaoStatusInvalida( + new TransicaoStatusInvalidaException( + StatusOrdemServico.ABERTA, + StatusOrdemServico.CONCLUIDA)); + + assertEquals(HttpStatus.CONFLICT.value(), problema.getStatus()); + assertEquals("Transição de status inválida", problema.getTitle()); + assertEquals( + "Não é permitido alterar o status de ABERTA para CONCLUIDA", + problema.getDetail()); + } } diff --git a/src/test/java/com/exemplo/ordem_servico/service/OrdemServicoServiceTests.java b/src/test/java/com/exemplo/ordem_servico/service/OrdemServicoServiceTests.java index 60d00bb..e020e2b 100644 --- a/src/test/java/com/exemplo/ordem_servico/service/OrdemServicoServiceTests.java +++ b/src/test/java/com/exemplo/ordem_servico/service/OrdemServicoServiceTests.java @@ -19,6 +19,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import com.exemplo.ordem_servico.exception.OrdemServicoNaoEncontradaException; +import com.exemplo.ordem_servico.exception.TransicaoStatusInvalidaException; import com.exemplo.ordem_servico.model.OrdemServico; import com.exemplo.ordem_servico.model.StatusOrdemServico; import com.exemplo.ordem_servico.repository.OrdemServicoRepository; @@ -114,12 +115,25 @@ void deveAtualizarStatusDaOrdem() { OrdemServico atualizada = service.atualizarStatus( 1L, - StatusOrdemServico.CONCLUIDA); + StatusOrdemServico.EM_ANDAMENTO); - assertEquals(StatusOrdemServico.CONCLUIDA, atualizada.getStatus()); + assertEquals(StatusOrdemServico.EM_ANDAMENTO, atualizada.getStatus()); verify(repository).save(ordem); } + @Test + void deveRecusarTransicaoDeStatusInvalida() { + OrdemServico ordem = novaOrdem("Cliente", "Servico", "1", "0"); + ordem.setId(1L); + when(repository.findById(1L)).thenReturn(Optional.of(ordem)); + + assertThrows( + TransicaoStatusInvalidaException.class, + () -> service.atualizarStatus( + 1L, + StatusOrdemServico.CONCLUIDA)); + } + private OrdemServico novaOrdem( String cliente, String descricao,