---
tags: [projeto, intranet, sessao, blog, gotechbr-site, nfse]
date: 2026-04-28
---
# Sessão 28/04/2026 — Blog GoTechBr + NFSe rejeitada
Duas frentes: imagens dos posts do blog (site GotechBr) que não carregavam, e NFSe rejeitada com erro de CEP.
## 1. Blog GoTechBr — imagens dos posts
### Sintoma
Cards do blog em `https://gotechbr.com.br/noticias` mostravam só o ícone de jornal (fallback), nenhuma foto.
### Causa raiz
- DB de produção tem 78 posts publicados, todos com `imagem_destaque` cadastrado
- Mas só **32** dos 46 nomes de arquivo existiam no disco em `uploads/site/blog/`
- 46 posts referenciavam arquivos inexistentes → 404 ao servir via rota `/site-img/blog/*`
### Por que faltavam arquivos
`BlogPostGenerator::buscarImagem()` tinha cadeia frágil:
1. Pixabay (chave estava OK no .env de produção, mas em algum momento falhou)
2. Fallback: `source.unsplash.com` — **descontinuado** desde 2024 (HTTP 503)
3. Sem terceira camada → posts ficavam com referência a arquivo nunca criado
### Fix 1 — substituir as 46 imagens faltantes por fotos reais
Script `replace_blog_pixabay.php` rodado em produção:
- Para cada um dos 46 IDs (33-78), buscou foto temática no Pixabay com termos em inglês mapeados por categoria
- Heurística do título corrige a categoria do DB (ex: "Auditoria SEO" → categoria=`seo`, não `desenvolvimento web`)
- Salvou com o **mesmo nome de arquivo** já cadastrado no DB → não precisou alterar a tabela
- Resultado: 46/46 OK, fotos reais (100KB-650KB cada), todas HTTP 200
Mapeamento de termos de busca por categoria fica em `replace_blog_pixabay.php` (no writable, auto-deletado).
### Fix 2 — patch do `BlogPostGenerator` para garantir imagem em posts futuros
`app/Libraries/BlogPostGenerator.php`:
- `buscarImagem()` virou cascata robusta:
1. **Pixabay** (foto de qualidade, chave `24153424-be1776f84b...` no .env do site)
2. **Loremflickr** — substitui `source.unsplash.com` morto. Foto Flickr CC, sem key
3. **GD branded** — método novo `gerarCapaGD()`. Cria capa local (gradiente verde GoTechBr + título escalado + tag de categoria colorida). Não depende de nada externo
- Tipo de retorno mudou de `?string` para `string` — nunca mais retorna null
- Trava extra no `gerarPost()`: `if (empty($imagem)) $imagem = $this->gerarCapaGD(...)` antes do INSERT. Garantia de que **fisicamente** não dá pra gravar post sem imagem
Pixabay key real está em `/home/gotechbr/public_html/.env` (não no .env da intranet). É necessária para a primeira camada da cadeia.
### Onde foi aplicado
- Arquivo deployado em `/home/gotechbr/public_html/app/Libraries/BlogPostGenerator.php`
- Backup `.bak.20260428` no servidor
## 2. NFSe rejeitada — CEP correto, mas cMun errado no XML
### Sintoma
NFSe ID 55 (cliente Juliana Maria Raia, CNPJ 19.235.953/0001-67, valor R$ 725,00) rejeitada pela SEFIN com erro:
> E0240: O CEP informado para o endereço nacional do tomador do serviço não existe ou não pertence ao município do endereço do tomador.
### Investigação
- CEP do cliente: 31720-490 (Belo Horizonte/MG, IBGE 3106200) — **válido**, ViaCEP confirma
- XML enviado para a SEFIN (`xml_envio` no DB) tinha:
- `31720490` ✅
- `3154606` ❌ — esse é o IBGE de **Ribeirão das Neves** (município do **prestador**, GoTechBR)
- SEFIN estava certa em rejeitar: CEP de BH não pertence a Ribeirão das Neves
### Causa raiz
Função `NfseNacionalApi::buscarCodigoMunicipioPorCep()` usava ViaCEP com timeout 5s. No momento da emissão (21/03 08:00) ViaCEP falhou (timeout/SSL hiccup pontual).
O fallback estava em `NfseNacionalApi::montarDPS` linha 603:
```php
'tomador_cMun' => $this->buscarCodigoMunicipioPorCep(...) ?? ($config['codigo_municipio'] ?? '3154606'),
```
Quando ViaCEP retornava null, **caía silenciosamente para o município do prestador** — bug grave porque permitia emissão com dado errado.
### Fix — 3 correções em paralelo
**A. Library `app/Libraries/NfseNacionalApi.php`**:
- `buscarCodigoMunicipioPorCep()` virou `resolverCodigoMunicipioCliente(array $cliente)`:
1. Lê `cliente['codigo_ibge']` primeiro (cache)
2. Se vazio: chama `buscarIbgePorCep()` que tenta ViaCEP (timeout 10s) → fallback **BrasilAPI** (`brasilapi.com.br/api/cep/v2/`)
3. Sucesso → persiste em `clientes.codigo_ibge` para próximas emissões
4. **Throw `RuntimeException`** se nada funcionar — emissão para em vez de mandar dado errado
- Funções novas: `buscarIbgePorCep()`, `cepViaCep()`, `cepBrasilApi()`
**B. Tabela `clientes`** — coluna nova:
```sql
ALTER TABLE clientes ADD COLUMN codigo_ibge CHAR(7) NULL AFTER cep, ADD INDEX idx_codigo_ibge (codigo_ibge)
```
- Adicionada em prod e local
- `ClienteModel::$allowedFields` atualizado para incluir `codigo_ibge`
- Script `preencher_ibge_clientes.php` rodou em prod e populou os 18 clientes ativos com IBGE correto
**C. Endpoint diagnóstico para reemissão silenciosa**:
- `Nfse::reemitirDiagnostico($id)` — token-guarded, igual padrão `cancelarDiagnostico`
- Marca a nota original como `cancelada` com motivo registrado
- Reemite com mesmos dados (cliente, valor, descrição, contrato)
- Passa flag `$notificarCliente=false` para `processarEmissao` — **suprime WhatsApp/email**
- Rota: `nfse/reemitir-diagnostico/(:num)` em `Routes.php`
- Excluída do auth em `Filters.php`
`processarEmissao` ganhou parâmetro `bool $notificarCliente = true`. Default mantém comportamento original; quem chama com `false` evita disparar mensagens.
### Reemissão da nota 55 (sem notificar cliente)
- URL: `https://sistema.gotechbr.com.br/nfse/reemitir-diagnostico/55?token=58253c6b...`
- Resultado:
- Nota 55 → status `cancelada`, motivo: `"E0240... | Reemitida sem notificar cliente"`
- Nota **102** (nova) → status `autorizada`, num_dps 69, chave `31546062228347294000141000000000005226040240187384`, valor R$ 725,00
- XML novo: `3106200` ✅ + `31720490` ✅
- Cliente 30: `codigo_ibge` cacheado = 3106200
- **Cliente Juliana NÃO recebeu notificação** (suprimida via flag)
### Deploy
- 4 arquivos: `NfseNacionalApi.php`, `Nfse.php`, `Routes.php`, `Filters.php`, `ClienteModel.php`
- Procedimento: tr -d CRLF → pscp /tmp → php -l → cp .bak.20260428 → mv → chown gotechbr:gotechbr
- OPcache reciclada via `pkill php-cgi` (servidor tem `validate_timestamps=0`)
## Pendentes
- Verificar se cron `*/30 8-21 * * *` de NFSe automática vai usar o cache de `codigo_ibge` corretamente nas próximas emissões (deveria — `montarDPS` lê `cliente['codigo_ibge']` agora)
- Se algum cliente novo for cadastrado sem CEP, a primeira emissão vai falhar com `RuntimeException` em vez de mandar dado errado — comportamento intencional, mas precisa validar no front que o CEP é obrigatório