# 03 — Sync Outbound (Nexos → Space)

> **"Configurações que atualizam direto o Space se eu mexer no core."**
>
> Sempre que um operador altera dados de colaborador no Nexos, o sistema **empurra** a mudança de volta
> ao Exent Space. Toda a lógica vive em [CollaboratorController.php](../../app/Http/Controllers/Core/CollaboratorController.php).

---

## 1. Os dois métodos de saída

### `syncProfileToSpace()` → `POST {exent_space_url_api}/nexos/profile-sync`

[CollaboratorController::syncProfileToSpace()](../../app/Http/Controllers/Core/CollaboratorController.php#L1157)

```php
public function syncProfileToSpace(CoreCollaborator $collaborator, array $newInfo,
                                   string $action = 'accepted', string $type = 'Informações pessoais')
```

- **Pré-condições:** o colaborador precisa ter `space_id` preenchido **e** as credenciais `exent_space`
  precisam existir. Caso contrário retorna `null` (não faz nada).
- **Monta o payload** a partir de `$newInfo` (campos alterados), descartando chaves `*_url` recebidas.
- Quando `action = 'accepted'`, gera **URLs assinadas (1 min)** dos documentos e adiciona como `*_url`.
- Mapeia `bank_account_data.bank_ispb` → `bank_account_data.ispb` (o Space espera a chave `ispb`).
- Acrescenta os campos de controle: `id_nexos`, `id_space`, `action`, `type`.

**Payload enviado:**

```json
{
  "name": "Novo Nome",
  "email": "novo@exent.com.br",
  "bank_account_data": {
    "bank_name": "Banco X", "bank_number": "001", "bank_agency": "1234",
    "bank_account": "56789", "bank_account_digit": "0", "account_type": "corrente",
    "pix_type": "cpf", "pix_key": "12345678900", "ispb": "00000000"
  },
  "doc_cnh_filename": "collaborator/fulano/documents/CNH_FULANO.pdf",
  "doc_cnh_filename_url": "https://storage.googleapis.com/...assinada",
  "id_nexos": 12,
  "id_space": 345,
  "action": "accepted",
  "type": "Informações pessoais, Documentos"
}
```

- `action`: `accepted` (aprovado/aplicado) ou `rejected` (recusado).
- Retorno: `['success' => true]` ou `['success' => false, 'message' => '...']`.

### `syncStatusToSpace()` → `POST {exent_space_url_api}/nexos/user-status`

[CollaboratorController::syncStatusToSpace()](../../app/Http/Controllers/Core/CollaboratorController.php#L1224)

```php
public function syncStatusToSpace(string $email, int $status)
```

**Payload enviado:**

```json
{ "email": "fulano@exent.com.br", "status": 1 }
```

- `status`: `1` = ativo, `0` = inativo.
- Retorno: `['success' => true]` ou `['success' => false, 'message' => '...']`.

---

## 2. Tabela de gatilhos — o que dispara o quê

| Ação no core | Método (controller) | Chamada ao Space | `action` |
|--------------|----------------------|------------------|----------|
| **Ativar / desativar** colaborador | [toggle_status()](../../app/Http/Controllers/Core/CollaboratorController.php#L485) | `/nexos/user-status` | — |
| **Aprovar** alteração pendente | [changes_approve()](../../app/Http/Controllers/Core/CollaboratorController.php#L1022) | `/nexos/profile-sync` | `accepted` |
| **Rejeitar** alteração pendente | [changes_reject()](../../app/Http/Controllers/Core/CollaboratorController.php#L1257) | `/nexos/profile-sync` | `rejected` |
| **Sync auto-aprovado** vindo do Space | [CollabController::sync()](../../app/Http/Controllers/Integrations/CollabController.php#L77) (via `terminating`) | `/nexos/profile-sync` | `accepted` |
| **Editar** colaborador (formulário) | [update()](../../app/Http/Controllers/Core/CollaboratorController.php#L696) | *(não chama direto — ver nota)* | — |

### Detalhe: edição manual (`update`) **não** chama o Space diretamente

Em [update()](../../app/Http/Controllers/Core/CollaboratorController.php#L696), ao salvar uma edição feita
pela tela do Nexos, o sistema **registra um histórico** `CoreCollabPendingChange` com `origin = 'nexos'` e
`status = 'approved'` (apenas se houver diff real e o colaborador estiver pareado com `space_id`). Esse
registro serve de auditoria; ele **não** dispara `syncProfileToSpace()` no mesmo fluxo.

---

## 3. Detalhe crítico: `terminating()` + `sleep(2)` no sync auto-aprovado

Em [CollabController::sync()](../../app/Http/Controllers/Integrations/CollabController.php#L327-L343), quando
a alteração vinda do Space é **não-sensível** (auto-aprovada), o Nexos aplica a mudança e precisa confirmar de
volta ao Space com `action = accepted`. Porém:

> O Space incrementa `pending_approval` na sua base **depois** de receber a resposta deste endpoint. Se o
> Nexos chamasse `syncProfileToSpace` imediatamente, o `accepted` chegaria **antes** do incremento e seria
> sobrescrito.

Por isso a chamada é adiada para **após** a resposta HTTP, via `app()->terminating()` com `sleep(2)`:

```php
app()->terminating(function () use ($collaboratorId, $payload, $type) {
    sleep(2);
    $collab = CoreCollaborator::find($collaboratorId);
    if (!$collab) return;
    (new \App\Http\Controllers\Core\CollaboratorController())
        ->syncProfileToSpace($collab, $payload, 'accepted', $type);
});
```

---

## 4. Comportamento de falha (best-effort)

As chamadas outbound **não revertem** a operação no core se o Space falhar:

- **`toggle_status`**: o status muda no Nexos normalmente; se o Space falhar, a mensagem de sucesso recebe
  um aviso anexado (`" Atenção: falha ao sincronizar status no Space: ..."`).
- **`changes_approve` / `changes_reject`**: a alteração já foi gravada no Nexos (commit). Se o sync falhar,
  o endpoint retorna **HTTP 502** com mensagem `"... mas falha ao sincronizar com o Space: ..."`, sinalizando
  ao operador que o Space ficou dessincronizado.
- **`sync` auto-aprovado**: roda no `terminating` (após resposta); falha não afeta o HTTP já devolvido.

> Resumo: a fonte de verdade é o Nexos. O sync ao Space é entregue com melhor esforço e sinaliza
> divergências, mas nunca desfaz o que já foi salvo no core.
