# Sincronização de ERP Read-Only com Controle Total no HELM

## Problema

O ERP é **somente leitura** e não podemos modificar nada nele:
- ✅ Podemos LER dados
- ❌ Não podemos escrever no ERP
- ❌ Não temos soft delete no ERP
- ❌ Quando usuário deleta no ERP, é **deleção física** (dado desaparece)

Solução: **Todo controle de integração fica no HELM**

---

## Arquitetura de Sincronização Read-Only

### 1. Tabela de Rastreamento (como chave de controle)

A tabela `integracao_rastreamento` é a **fonte de verdade** do HELM:

```sql
integracao_rastreamento {
  chave_origem: PK do ERP (ex: 26598)
  cdregistro_destino: PK do HELM (lancamento gerado)
  flstatus: 'ativo' | 'deletado' | 'modificado'
  hash_dados: SHA-256 dos dados
  qttenativas_reprocessamento: contador de tentativas
}
```

**Fluxo:**
```
ERP (read-only)
    ↓
    ├─ Chave 26598 → encontrada no ERP
    │   ├─ Hash mudou? → status 'modificado'
    │   └─ Hash igual? → status 'ativo'
    │
    └─ Chave 26598 → NÃO encontrada no ERP
        └─ Verificar em rastreamento
           └─ Se estava 'ativo' → deletar no HELM
```

---

## Algoritmo de Sincronização Read-Only

### Fase 1: Buscar Dados Novos/Modificados (com incremento)

```php
// Trazer apenas dados NOVOS ou MODIFICADOS
$sql_origem = "
  SELECT 
    id as chave_origem,
    campos...
  FROM lancamentos
  WHERE data_criacao > :ultima_sinc_sucesso
     OR data_modificacao > :ultima_sinc_sucesso
";

// Resultado: ~45K-50K records (incremento)
$registros_novos = $pdo_erp->execute($sql_origem, [
    ':ultima_sinc_sucesso' => $integracao_config->ultima_sinc_sucesso
]);
```

**Processamento:**
- Para cada chave: Calcular hash
- Se existe em `rastreamento` E hash igual → PULAR (não mudou)
- Se existe em `rastreamento` E hash diferente → Marcar 'modificado', adicionar UPDATE à fila
- Se NÃO existe em `rastreamento` → Adicionar INSERT à fila, registrar em rastreamento

---

### Fase 2: Detectar Deleções (CRÍTICO para read-only)

Essa é a fase **mais importante** para seu caso:

```php
// Buscar TODAS as chaves que ainda existem no ERP
// OBS: Essa query retorna apenas IDs, é muito rápida
$sql_chaves_ativas = "
  SELECT id as chave_origem
  FROM lancamentos
  ORDER BY id
";

// Resultado: ~1M-2M chaves (mas muito leve, só o ID)
$chaves_erp = [];
foreach ($pdo_erp->execute($sql_chaves_ativas) as $row) {
    $chaves_erp[] = $row['chave_origem'];
}

// Buscar chaves que estavam ATIVAS no rastreamento
$stmt = $pdo->prepare("
  SELECT chave_origem, cdregistro_destino
  FROM integracao_rastreamento
  WHERE cdintegracao = :cdintegracao
    AND flstatus = 'ativo'
");
$stmt->execute([':cdintegracao' => $cdintegracao]);

$chaves_rastreadas = [];
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
    $chaves_rastreadas[$row['chave_origem']] = $row['cdregistro_destino'];
}

// COMPARAR: Quais chaves existem no rastreamento mas NÃO estão mais no ERP?
$chaves_deletadas = array_diff_key($chaves_rastreadas, array_flip($chaves_erp));

// Para cada chave deletada no ERP: adicionar DELETE à fila
foreach ($chaves_deletadas as $chave => $cdregistro_dest) {
    $fila = new IntegracaoFila();
    $fila->cdintegracao = $cdintegracao;
    $fila->tipo_operacao = 'DELETE';
    $fila->dados_json = json_encode([
        'cdregistro_destino' => $cdregistro_dest,
        'chave_origem' => $chave,
        'motivo' => 'Deletado no ERP'
    ]);
    $fila->flstatus = 'pendente';
    $fila->save();
    
    // Marcar como deletado no rastreamento
    $stmt = $pdo->prepare("
      UPDATE integracao_rastreamento
      SET flstatus = 'deletado', dthdelecao = NOW()
      WHERE chave_origem = :chave AND cdintegracao = :cdintegracao
    ");
    $stmt->execute([
        ':chave' => $chave,
        ':cdintegracao' => $cdintegracao
    ]);
}
```

---

## Exemplo Real: Integração de Lançamento

### Cenário 1: Novo lançamento

```
Usuário no ERP cria lançamento #26598
    ↓
SincronizacaoERP.php:
  1. Busca dados novos (data_criacao > última_sinc)
  2. Encontra #26598
  3. Calcula hash: "abc123..."
  4. Verifica em rastreamento: NÃO EXISTE
  5. Adiciona INSERT à fila com dados
  6. Registra em rastreamento: chave_origem=26598, hash=abc123, status='ativo'
    ↓
FilaProcessador.php:
  1. Processa INSERT
  2. Cria lançamento no HELM
  3. Registra cdregistro_destino=456 em rastreamento
```

**Rastreamento após:**
```
chave_origem: 26598
cdregistro_destino: 456 (lancamento no HELM)
flstatus: 'ativo'
hash_dados: abc123...
```

---

### Cenário 2: Lançamento modificado

```
Usuário no ERP modifica lançamento #26598 (altera valor)
    ↓
SincronizacaoERP.php:
  1. Busca dados modificados (data_modificacao > última_sinc)
  2. Encontra #26598
  3. Calcula novo hash: "xyz789..."
  4. Verifica em rastreamento: EXISTE
  5. Hash anterior: "abc123..." ≠ novo hash: "xyz789..."
  6. Adiciona UPDATE à fila com novos dados
  7. Atualiza rastreamento: hash=xyz789, status='modificado'
    ↓
FilaProcessador.php:
  1. Processa UPDATE
  2. Atualiza lançamento #456 no HELM
  3. Marca rastreamento: status='ativo' (sync concluído)
```

**Rastreamento após:**
```
chave_origem: 26598
cdregistro_destino: 456
flstatus: 'ativo'
hash_dados: xyz789...  ← ATUALIZADO
```

---

### Cenário 3: Lançamento deletado no ERP (CRÍTICO)

```
Usuário no ERP deleta lançamento #26598
(Não pode usar soft delete, ERP é read-only)
    ↓
Próxima sincronização:

SincronizacaoERP.php Fase 2 (Detectar Deleções):
  1. Executa: SELECT id FROM lancamentos (lista TODAS as chaves ativas)
  2. Resultado: [1, 2, 3, 4, 5, ...] ← NÃO inclui 26598 (foi deletado)
  3. Busca rastreamento: [1, 2, 3, 4, 5, 26598] ← INCLUI 26598 (estava lá)
  4. Calcula diferença: 26598 está em rastreamento mas NÃO está em ERP
  5. Conclusão: #26598 foi DELETADO no ERP
  6. Adiciona DELETE à fila
  7. Marca rastreamento: status='deletado', dthdelecao=NOW()
    ↓
FilaProcessador.php:
  1. Processa DELETE
  2. Soft delete no HELM: UPDATE lancamento SET flvigente=0 WHERE cdlancamento=456
  3. Rastreamento marca: status='deletado'
```

**Rastreamento após:**
```
chave_origem: 26598
cdregistro_destino: 456
flstatus: 'deletado'  ← MARCADO COMO DELETADO
dthdelecao: 2026-01-10 14:30:00
```

---

## Implementação no SincronizacaoERP.php

### Mudança Principal: Adicionar Fase de Deteção de Deleções

```php
public function iniciar() {
    // ... fases anteriores ...
    
    // Fase 4: DETECTAR DELEÇÕES (novo para read-only)
    $this->sincronizarDeletadosReadOnly();
    
    // ... resto do código ...
}

private function sincronizarDeletadosReadOnly() {
    // 1. Buscar TODAS as chaves que ainda existem no ERP
    $erp_conn = $this->conectarERP();
    
    $stmt = $erp_conn->query("
        SELECT id as chave_origem 
        FROM lancamentos 
        ORDER BY id
    ");
    
    $chaves_erp = [];
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        $chaves_erp[] = $row['chave_origem'];
    }
    
    // Converter para array associativo para usar array_flip
    $chaves_erp_flip = array_flip($chaves_erp);
    
    // 2. Buscar chaves ativas no rastreamento
    $stmt = $this->pdo->prepare("
        SELECT chave_origem, cdregistro_destino
        FROM integracao_rastreamento
        WHERE cdintegracao = :cdintegracao
          AND flstatus = 'ativo'
    ");
    $stmt->execute([':cdintegracao' => $this->cdintegracao]);
    
    $deletados = 0;
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        // Se a chave NÃO existe mais no ERP
        if (!isset($chaves_erp_flip[$row['chave_origem']])) {
            // Adicionar DELETE à fila
            $this->adicionarFila(
                'DELETE',
                [
                    'cdregistro_destino' => $row['cdregistro_destino'],
                    'chave_origem' => $row['chave_origem'],
                    'motivo' => 'Deletado no ERP (read-only)'
                ]
            );
            
            // Marcar como deletado no rastreamento
            $stmt_upd = $this->pdo->prepare("
                UPDATE integracao_rastreamento
                SET flstatus = 'deletado', dthdelecao = NOW()
                WHERE chave_origem = :chave
                  AND cdintegracao = :cdintegracao
            ");
            $stmt_upd->execute([
                ':chave' => $row['chave_origem'],
                ':cdintegracao' => $this->cdintegracao
            ]);
            
            $deletados++;
        }
    }
    
    // Log
    error_log("[SincronizacaoERP] Deleções detectadas (read-only): $deletados");
}
```

---

## Performance: Detectando Deleções em 1M+ registros

### Problema: Comparar 1M+ chaves é lento?

**Não!** Por quê:

```php
// Operação 1: Buscar todas as chaves do ERP
// Query rápida: SELECT id (apenas coluna pequena, sem JOIN)
// Resultado: ~2M chaves em ~10-20 segundos
// Resultado em memória: ~50MB (um ID por linha)

// Operação 2: Buscar chaves ativas no rastreamento
// Query rápida: SELECT chave_origem WHERE flstatus='ativo'
// Resultado: ~1.8M chaves em ~5 segundos
// Resultado em memória: ~50MB

// Operação 3: array_diff_key em PHP
// Comparação em memória: muito rápido (~2 segundos)
// Resultado: lista pequena de deletados (~1-2%)

// Total: ~30-40 segundos por sincronização
// Todos os sincronizadores podem rodar em paralelo
```

### Otimizações:

1. **Usar índices:**
```sql
CREATE INDEX idx_integracao_rastreamento_status 
ON integracao_rastreamento(cdintegracao, flstatus);
```

2. **Buscar apenas IDs (sem dados):**
```php
// ✅ Rápido: Apenas a chave
SELECT id FROM lancamentos

// ❌ Lento: Todos os campos
SELECT * FROM lancamentos
```

3. **Processar em lotes:**
```php
// Em vez de comparar 1M de uma vez
// Processar em chunks de 10K
$chaves_chunk = array_chunk($chaves_erp, 10000);
foreach ($chaves_chunk as $chunk) {
    $this->detectarDeletadosEmLote($chunk);
}
```

---

## Fluxo Completo de Sincronização (Read-Only)

```
SINCRONIZAÇÃO READ-ONLY (1-2 horas para 1M registros)
│
├─ FASE 1: Buscar dados novos/modificados
│   ├─ SELECT data_criacao > última_sinc
│   ├─ SELECT data_modificacao > última_sinc
│   ├─ Resultado: ~45K-50K registros (3-5% do total)
│   ├─ Para cada: Calcular hash
│   ├─ Se novo → INSERT para fila
│   ├─ Se modificado → UPDATE para fila
│   └─ Tempo: ~5-10 minutos
│
├─ FASE 2: Detectar deleções (ler-only)
│   ├─ SELECT id FROM lancamentos (todas as chaves)
│   ├─ Resultado: ~1.8M-2M chaves
│   ├─ SELECT chave_origem FROM rastreamento WHERE flstatus='ativo'
│   ├─ Comparar: quais estavam em rastreamento mas não estão em ERP?
│   ├─ Resultado: ~1-2% deletados (~18K-40K)
│   ├─ Para cada deletado: DELETE para fila
│   └─ Tempo: ~30-40 minutos
│
├─ FASE 3: Adicionar à fila
│   ├─ INSERT INTO integracao_fila (tipo_operacao, dados_json...)
│   ├─ Total pendente: ~45K INSERT + ~30K UPDATE + ~20K DELETE = ~95K
│   └─ Tempo: ~5 minutos
│
├─ FASE 4: Finalizar log
│   ├─ UPDATE integracao_log SET dthfim=NOW(), flstatus='sucesso'
│   └─ Tempo: ~1 minuto
│
└─ FILA PENDENTE: ~95K operações
    │
    └─ FilaProcessador.php (roda continuamente)
        ├─ Processa 100-500 por vez
        ├─ INSERT: validação FK, registrar em rastreamento
        ├─ UPDATE: validação, comparar hash, atualizar
        ├─ DELETE: soft delete, marcar rastreamento
        └─ 5 workers em paralelo processam fila em ~5-10 min

RESULTADO:
├─ Até 1M registros sincronizados
├─ Deleções detectadas e propagadas
├─ Audit trail completo em integracao_log
└─ Rastreamento 100% consistente com HELM
```

---

## Configuração do integracao_config_sinc

```sql
INSERT INTO back.integracao_config_sinc 
(cdintegracao, cdempresa, frequencia_minutos, fl_sincronizar_delecoes, qt_registros_lote)
VALUES
(1, 1, 60, true, 1000);
-- Sincroniza a cada 60 minutos
-- Detecta deleções (read-only)
-- Processa em lotes de 1000
```

---

## Monitoramento: Verificar Deleções Detectadas

```sql
-- Ver deleções detectadas nesta sincronização
SELECT 
  l.dthsinc,
  COUNT(*) as deletados_detectados
FROM integracao_log l
JOIN integracao_rastreamento r 
  ON r.cdintegracao = l.cdintegracao
WHERE r.dthdelecao > l.dthinicio
  AND r.dthdelecao < l.dthfim
GROUP BY l.dthsinc
ORDER BY l.dthsinc DESC;

-- Ver últimas deleções detectadas
SELECT 
  chave_origem,
  cdregistro_destino,
  dthdelecao,
  'Deletado no ERP' as motivo
FROM integracao_rastreamento
WHERE flstatus = 'deletado'
ORDER BY dthdelecao DESC
LIMIT 20;
```

---

## Resumo das Mudanças

| Aspecto | Antes (com soft delete) | Depois (read-only) |
|--------|-------------------------|-------------------|
| **Detectar deleção** | Procurar por `data_delecao IS NOT NULL` | Procurar por ausência (diff array) |
| **Query** | Buscar dados deletados | Buscar IDs de tudo que existe |
| **Performance** | Depende da coluna data_delecao | Rápido: ~40 min para 1M |
| **Confiabilidade** | Depende do ERP marcar soft delete | 100% confiável: falta = deletado |
| **Recuperação** | Possível (ainda tem dados no ERP) | Não possível (dado foi deletado) |

---

## Checklist de Implementação

- [ ] Adicionar método `sincronizarDeletadosReadOnly()` em SincronizacaoERP.php
- [ ] Criar índice em `integracao_rastreamento(cdintegracao, flstatus)`
- [ ] Testar com 1000 registros
- [ ] Testar deletando registros no ERP
- [ ] Verificar rastreamento marca como deletado
- [ ] Verificar HELM faz soft delete
- [ ] Monitorar tempo de execução (deve ser <60 min para 1M)
- [ ] Configurar cron job com frequência desejada
- [ ] Implementar alertas se demorar >2 horas

