# 🔧 Correções Críticas Necessárias - Módulo de Orçamento

**Data:** 2025-01-XX  
**Prioridade:** 🔴 CRÍTICO  
**Baseado em:** `ANALISE_TECNICA_PROFUNDA_ORCAMENTO.md`

---

## 🚨 Problema Crítico #1: Cálculo Incorreto de totalCommitted

### Problema Identificado

**Arquivo:** `sigefp-budget/src/main/java/br/gov/sigefp/budget/domain/BudgetLine.java` (Linha 49)

```java
// ❌ ERRADO - Soma TODOS os tipos de movimento
@org.hibernate.annotations.Formula("(SELECT COALESCE(SUM(bex.amount), 0) FROM budget_execution bex WHERE bex.budget_line_id = id)")
private BigDecimal totalCommitted;
```

**Impacto:**
- Soma COMMITMENT + LIQUIDATION + PAYMENT
- `availableBalance = totalAllocated - totalCommitted` fica **INCORRETO**
- Permite criar COMMITMENTs além do disponível
- **Quebra integridade orçamentária**

### Correção

```java
// ✅ CORRETO - Soma apenas COMMITMENT
@org.hibernate.annotations.Formula("(SELECT COALESCE(SUM(bex.amount), 0) FROM budget_execution bex WHERE bex.budget_line_id = id AND bex.movement_type = 'COMMITMENT')")
private BigDecimal totalCommitted;
```

**Nota:** O `BudgetExecutionRepository.calculateTotalCommittedByBudgetLineId()` já está correto (filtra por COMMITMENT), mas o @Formula não.

---

## 🚨 Problema Crítico #2: Falta Validação de Sequência

### Problema Identificado

**Arquivo:** `sigefp-budget/src/main/java/br/gov/sigefp/budget/service/BudgetExecutionService.java`

**Cenário Atual:**
- ✅ Valida COMMITMENT (saldo disponível)
- ❌ **NÃO valida** se existe COMMITMENT antes de criar LIQUIDATION
- ❌ **NÃO valida** se existe LIQUIDATION antes de criar PAYMENT
- ❌ Permite criar PAYMENT sem COMMITMENT/LIQUIDATION

**Impacto:**
- Quebra ciclo de execução orçamentária (COMMITMENT → LIQUIDATION → PAYMENT)
- Permite "pular" etapas
- **Não conforma com normas GFP**

### Correção Necessária

Adicionar validações no método `registerExecution()`:

```java
// Após validação de COMMITMENT (linha 59)

// Validação 3: Sequência para LIQUIDATION
if ("LIQUIDATION".equals(dto.getMovementType())) {
    if (dto.getReferenceId() == null) {
        throw new BusinessException("ReferenceId é obrigatório para LIQUIDATION",
                "MISSING_REFERENCE_ID", HttpStatus.BAD_REQUEST);
    }
    
    // Buscar COMMITMENT correspondente
    List<BudgetExecution> commitments = budgetExecutionRepository
            .findByBudgetLineIdAndMovementType(budgetLine.getId(), "COMMITMENT");
    
    // Filtrar por referenceId se fornecido
    BigDecimal totalCommitted = commitments.stream()
            .filter(c -> dto.getReferenceId().equals(c.getReferenceId()))
            .map(BudgetExecution::getAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    
    if (totalCommitted.compareTo(BigDecimal.ZERO) == 0) {
        throw new BusinessException("Não existe COMMITMENT correspondente para liquidar",
                "NO_COMMITMENT", HttpStatus.CONFLICT);
    }
    
    // Calcular total já liquidado
    List<BudgetExecution> liquidations = budgetExecutionRepository
            .findByBudgetLineIdAndMovementType(budgetLine.getId(), "LIQUIDATION");
    BigDecimal totalLiquidated = liquidations.stream()
            .filter(l -> dto.getReferenceId().equals(l.getReferenceId()))
            .map(BudgetExecution::getAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    
    BigDecimal availableToLiquidate = totalCommitted.subtract(totalLiquidated);
    if (dto.getAmount().compareTo(availableToLiquidate) > 0) {
        throw new BusinessException(
            String.format("Liquidação não pode exceder empenho. Disponível: %s, Solicitado: %s",
                availableToLiquidate, dto.getAmount()),
            "INSUFFICIENT_COMMITMENT", HttpStatus.CONFLICT);
    }
}

// Validação 4: Sequência para PAYMENT
if ("PAYMENT".equals(dto.getMovementType())) {
    if (dto.getReferenceId() == null) {
        throw new BusinessException("ReferenceId é obrigatório para PAYMENT",
                "MISSING_REFERENCE_ID", HttpStatus.BAD_REQUEST);
    }
    
    // Buscar LIQUIDATION correspondente
    List<BudgetExecution> liquidations = budgetExecutionRepository
            .findByBudgetLineIdAndMovementType(budgetLine.getId(), "LIQUIDATION");
    
    BigDecimal totalLiquidated = liquidations.stream()
            .filter(l -> dto.getReferenceId().equals(l.getReferenceId()))
            .map(BudgetExecution::getAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    
    if (totalLiquidated.compareTo(BigDecimal.ZERO) == 0) {
        throw new BusinessException("Não existe LIQUIDATION correspondente para pagar",
                "NO_LIQUIDATION", HttpStatus.CONFLICT);
    }
    
    // Calcular total já pago
    List<BudgetExecution> payments = budgetExecutionRepository
            .findByBudgetLineIdAndMovementType(budgetLine.getId(), "PAYMENT");
    BigDecimal totalPaid = payments.stream()
            .filter(p -> dto.getReferenceId().equals(p.getReferenceId()))
            .map(BudgetExecution::getAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    
    BigDecimal availableToPay = totalLiquidated.subtract(totalPaid);
    if (dto.getAmount().compareTo(availableToPay) > 0) {
        throw new BusinessException(
            String.format("Pagamento não pode exceder liquidação. Disponível: %s, Solicitado: %s",
                availableToPay, dto.getAmount()),
            "INSUFFICIENT_LIQUIDATION", HttpStatus.CONFLICT);
    }
}
```

**Nota:** Para melhor performance, seria ideal criar métodos no repository:
- `calculateTotalCommittedByReferenceId(UUID referenceId)`
- `calculateTotalLiquidatedByReferenceId(UUID referenceId)`
- `calculateTotalPaidByReferenceId(UUID referenceId)`

---

## 🟡 Problema Médio #3: Tratamento de Exceções

### Correção BudgetEntryService

**Arquivo:** `sigefp-budget/src/main/java/br/gov/sigefp/budget/service/BudgetEntryService.java`

```java
// ❌ ANTES
.orElseThrow(() -> new IllegalArgumentException("Linha orçamentária não encontrada: " + dto.getBudgetLineId()));

// ✅ DEPOIS
import br.gov.sigefp.common.exception.ResourceNotFoundException;

.orElseThrow(() -> new ResourceNotFoundException("Linha orçamentária não encontrada: " + dto.getBudgetLineId()));
```

### Correção BudgetEntryController

**Arquivo:** `sigefp-budget/src/main/java/br/gov/sigefp/budget/api/BudgetEntryController.java`

```java
// ❌ ANTES
catch (IllegalArgumentException e) {
    return ResponseEntity.badRequest().build();
}

// ✅ DEPOIS
import br.gov.sigefp.common.exception.ResourceNotFoundException;
import br.gov.sigefp.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BudgetEntryController {
    // ...
    
    @PostMapping
    public ResponseEntity<BudgetEntryDTO> create(@Valid @RequestBody CreateBudgetEntryDTO dto) {
        try {
            BudgetEntryDTO created = budgetEntryService.create(dto);
            return ResponseEntity.status(HttpStatus.CREATED).body(created);
        } catch (ResourceNotFoundException e) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        } catch (BusinessException e) {
            return ResponseEntity.status(e.getHttpStatus()).build();
        } catch (Exception e) {
            log.error("Erro inesperado ao criar entrada orçamentária", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}
```

---

## 🟡 Problema Médio #4: BudgetIntegrationService

**Arquivo:** `sigefp-budget/src/main/java/br/gov/sigefp/budget/integration/BudgetIntegrationService.java`

```java
// ❌ ANTES
catch (Exception e) {
    log.error("Erro ao criar execução orçamentária (COMMITMENT): {}", e.getMessage());
    throw new RuntimeException("Erro ao criar execução orçamentária: " + e.getMessage(), e);
}

// ✅ DEPOIS
catch (InsufficientBudgetException e) {
    log.error("Saldo insuficiente ao criar execução orçamentária (COMMITMENT)", e);
    throw e; // Re-throw exceção específica
}
catch (BusinessException e) {
    log.error("Erro de negócio ao criar execução orçamentária (COMMITMENT)", e);
    throw e;
}
catch (Exception e) {
    log.error("Erro inesperado ao criar execução orçamentária (COMMITMENT)", e);
    throw new BusinessException("Erro ao criar execução orçamentária", 
            "EXECUTION_ERROR", HttpStatus.INTERNAL_SERVER_ERROR);
}
```

---

## 🟡 Problema Médio #5: Validação de TRANSFER_OUT

**Arquivo:** `sigefp-budget/src/main/java/br/gov/sigefp/budget/service/BudgetEntryService.java`

**Problema:** Não valida se TRANSFER_OUT não excede saldo disponível.

**Correção:**

```java
public BudgetEntryDTO create(CreateBudgetEntryDTO dto) {
    BudgetLine budgetLine = budgetLineRepository.findById(dto.getBudgetLineId())
            .orElseThrow(() -> new ResourceNotFoundException(
                    "Linha orçamentária não encontrada: " + dto.getBudgetLineId()));

    // Validação: TRANSFER_OUT não pode exceder saldo disponível
    if (dto.getType() == BudgetEntryType.TRANSFER_OUT || 
        dto.getType() == BudgetEntryType.CANCELLATION) {
        
        BigDecimal totalAllocated = budgetAllocationRepository
                .calculateTotalAllocatedByBudgetLineId(budgetLine.getId());
        BigDecimal totalCommitted = budgetExecutionRepository
                .calculateTotalCommittedByBudgetLineId(budgetLine.getId());
        BigDecimal available = totalAllocated.subtract(totalCommitted);
        
        if (dto.getAmount().compareTo(available) > 0) {
            throw new BusinessException(
                String.format("Transferência/Cancelamento não pode exceder saldo disponível. Disponível: %s, Solicitado: %s",
                    available, dto.getAmount()),
                "INSUFFICIENT_BALANCE", HttpStatus.CONFLICT);
        }
    }
    
    // ... resto do código
}
```

---

## 📋 Resumo das Correções

### 🔴 Críticas (Urgente)
1. ✅ Corrigir `@Formula` de `totalCommitted` para filtrar apenas COMMITMENT
2. ✅ Adicionar validação de sequência LIQUIDATION (exige COMMITMENT)
3. ✅ Adicionar validação de sequência PAYMENT (exige LIQUIDATION)

### 🟡 Médias (Importante)
4. ✅ Substituir `IllegalArgumentException` por exceções customizadas
5. ✅ Melhorar tratamento de erros nos controllers
6. ✅ Melhorar `BudgetIntegrationService` (re-throw exceções específicas)
7. ✅ Adicionar validação de TRANSFER_OUT/CANCELLATION

---

**Documento gerado em:** 2025-01-XX  
**Versão:** 1.0

