# Documentação técnica — Jobs em fila, comandos Artisan e agendamento (schedule)

Este documento padroniza o que antes estava em `docs/job-contratos-receitas-despesas.md` (nome legado) e **inventaria todo processamento em background** do repositório: **um Job de fila** (`app/Jobs`), **comandos Artisan agendados** em `app/Console/Kernel.php` e comandos **manuais** relacionados (classificação contábil). Não existem outros arquivos em `app/Jobs/` além do listado abaixo.

**Documentação de domínio relacionada:** contratos e recorrência — **`Doc_Modulo_Contrato.md`**; NF, parcelas e fluxo financeiro — **`Doc_Modulo_Financeiro.md`**; demais módulos na raiz com prefixo **`Doc_Modulo_*.md`**. Em `docs/` permanecem materiais pontuais (ex.: `docs/comparacao-pacotes-status.md`).

---

## 1. Resumo executivo

| Tipo | Nome | Disparo | Requer |
|------|------|---------|--------|
| **Fila (queue)** | `ProcessarPontuacaoCampanhaJob` | Após resposta HTTP, quando `Nf` é atualizada e passa a ser “venda concluída” (ver § 2) | Worker: `php artisan queue:work` (ou Horizon, etc.) |
| **Scheduler** | `assinatura:gerar-cobrancas` | `everyMinute()` no `Kernel` | Cron: `* * * * * php artisan schedule:run` |
| **Scheduler** | `app:encerrar-campanhas-expiradas` | `dailyAt('02:00')` no `Kernel` | Idem |
| **Scheduler** | `cartao:faturas-gerar --meses=2 --historico=2` | `dailyAt('03:00')` no `Kernel` | Competências de fatura; ver **`Doc_Modulo_Financeiro.md`** § cartão |
| **Scheduler + fila** | `dfe:sincronizar` | `dailyAt` conforme `DFE_DAILY_AT` (padrão `03:30`) | Worker de fila; ver **`Doc_Modulo_Caixa_Entrada_Fiscal.md`** |
| **Manual** | `contabilidade:sincronizar-classificacao` | Não agendado | Propaga DRE/DFC para `grupoconta` / `prodservtipo` — **`Doc_Modulo_Produto_Servico.md`** |

---

## 2. Job em fila — `ProcessarPontuacaoCampanhaJob`

**Arquivo:** `app/Jobs/ProcessarPontuacaoCampanhaJob.php`

**Interface:** `Illuminate\Contracts\Queue\ShouldQueue` (executado de forma assíncrona na fila padrão).

**Construtor:** recebe um `Nf`; na serialização guarda apenas `idnf`.

**`handle`:** recarrega a NF por `id`; se existir, delega a **`ProcessaPontuacaoCampanha::processarNf`** (`app/Services/ProcessaPontuacaoCampanha.php`).

**Disparo:** no model **`Nf`**, listener `updated` (`app/Models/Nf.php`): quando certos atributos mudam (`status`, `saida`, `dataentrada`, `idempresa`, `criadopor`) **e** a NF satisfaz venda concluída (`tipo = C`, `saida = V`, status `CONCLUÍDO` ou `CONCLUIDO`), chama:

`ProcessarPontuacaoCampanhaJob::dispatchAfterResponse($nf)`.

**Efeito de negócio:** pontuação de **campanhas** (produtos elegíveis, empresas participantes, logs idempotentes). Logs em `storage/logs/laravel.log` (mensagens com emoji no serviço são esperadas).

**Operação:** sem worker ativo, o job fica na tabela `jobs` (driver `database`) ou no Redis, conforme `.env` — **não roda sozinho**.

**Validação na fila:** `ProcessaPontuacaoCampanha::ehVendaValida` usa o mesmo critério de status que **`Nf::ehVendaConcluida`** (`CONCLUÍDO` e `CONCLUIDO`, após `trim` + `mb_strtoupper`).

---

## 3. Comando — `assinatura:gerar-cobrancas` (contratos → NF / parcelas / itens)

**Arquivo:** `app/Console/Commands/GerarCobrancaAssinatura.php`  
**Assinatura:** `php artisan assinatura:gerar-cobrancas {--debug}`

**O que faz (alinhado ao código):**

1. Carrega **todos** os contratos com **`status = ATIVO`**.
2. Para cada contrato, obtém **`getProximaDataRecorrencia()`** (Carbon ou `null`). Equivale logicamente a **`precisaGerarCobranca()`** no model (`(bool) getProximaDataRecorrencia()`), mas o comando **não** chama o método `precisaGerarCobranca` pelo nome.
3. Se houver data:
   - Abre **transação** por contrato;
   - Chama **`ContratoService::processarRecorrencias($contrato)`** (NF, `nfitem`, `nfparcela`, atualização de **`dataultimarecorrencia`** conforme implementação do serviço);
   - **Commit**; em exceção, **rollback** e log: `"Erro ao gerar as cobrancas (contrato {id}): ..."` (e mensagem no console com `--debug` ou erro).

**`--debug`:** imprime quantidade de ativos, e por contrato se **não gera** (recorrência inválida ou próxima data nula com contexto de datas) ou se **gera** com o vencimento.

**Saída:** se não houver ativos, aviso *"Nenhum contrato com status ATIVO"*. Resumo final quando `--debug` ou quando `gerados > 0`.

**Agendamento:** `app/Console/Kernel.php` — **`$schedule->command('assinatura:gerar-cobrancas')->everyMinute();`**

**Conferência:**

- Manual: `php artisan assinatura:gerar-cobrancas` ou com `--debug`.
- Banco: `contrato.dataultimarecorrencia`, novas linhas em `nf` com `idcontrato`, `nfparcela`, `nfitem`.
- Logs: `storage/logs/laravel.log` (erros por contrato; mensagens propagadas do `ContratoService` / `NfService` conforme ocorrência).

**Arquivos principais:** `ContratoService`, `NfService`, `Contrato` — detalhes em **`Doc_Modulo_Contrato.md`** e **`Doc_Modulo_Financeiro.md`**.

---

## 4. Comando — `app:encerrar-campanhas-expiradas`

**Arquivo:** `app/Console/Commands/EncerrarCampanhasExpiradas.php`  
**Assinatura:** `php artisan app:encerrar-campanhas-expiradas`

**O que faz:** para `Campanha` com **`status != 'encerrada'`** (comparação **case-sensitive** no SQL) e **`datafim` anterior a hoje** (data), atualiza **`status` para `'ENCERRADA'`** (uppercase no update).

**Saída:** mensagem informativa com quantidade encerrada ou *"Nenhuma campanha expirou hoje."*

**Agendamento:** `Kernel` — **`dailyAt('02:00')`**.

**Observação:** há mistura de caixa (`encerrada` vs `ENCERRADA`) entre filtro e valor gravado; vale padronizar no código futuramente se houver campanhas com status em formato misto.

---

## 5. Comando — `cartao:faturas-gerar` (faturas de cartão corporativo)

**Arquivo:** `app/Console/Commands/GerarFaturasCartao.php`  
**Assinatura:** `php artisan cartao:faturas-gerar {--meses=2} {--historico=2}`

**O que faz:** para cada **`cartao_credito`** ativo, chama **`CartaoFaturaService::garantirFaturasDeTodosCartoesAtivos`**: cria/sincroniza competências em **`cartao_fatura`** (meses futuros e histórico), recalcula valores e aplica fechamento automático quando aplicável.

**Agendamento:** `Kernel` — **`dailyAt('03:00')`** com `--meses=2 --historico=2`.

**Domínio:** **`Doc_Modulo_Financeiro.md`** § 1.10.

---

## 6. Comando — `contabilidade:sincronizar-classificacao` (manual)

**Arquivo:** `app/Console/Commands/SincronizarClassificacaoContabil.php`  
**Assinatura:** `php artisan contabilidade:sincronizar-classificacao {--sem-sobrescrever}`

**O que faz:** delega a **`ClassificacaoContabilService::sincronizarCadastros`**, atualizando **`grupoconta`** (`idlinhadre`, `tipo_fluxo`) e **`prodservtipo`** (`idlinhadre`) a partir de **`classificacao_contabil_padrao`** e nomes do cadastro.

**Não** está no schedule — rodar após deploy de seed/migration de classificação ou alteração dos parâmetros globais.

---

## 7. Schedule completo (`app/Console/Kernel.php`)

```php
$schedule->command('assinatura:gerar-cobrancas')->everyMinute();
$schedule->command('app:encerrar-campanhas-expiradas')->dailyAt('02:00');
$schedule->command('cartao:faturas-gerar --meses=2 --historico=2')->dailyAt('03:00');
$schedule->command('dfe:sincronizar')->dailyAt(config('dfe.daily_at', '03:30'));
```

**Produção:** uma entrada cron por minuto apontando para `php artisan schedule:run` no diretório do projeto (usuário e PHP corretos).

---

## 8. Referência rápida de arquivos

| Arquivo | Função |
|---------|--------|
| `app/Jobs/ProcessarPontuacaoCampanhaJob.php` | Job em fila — pontuação por NF |
| `app/Services/ProcessaPontuacaoCampanha.php` | Regras de campanha / pontos |
| `app/Models/Nf.php` | Dispara o job após `updated` (venda concluída) |
| `app/Console/Commands/GerarCobrancaAssinatura.php` | Cobranças de contrato |
| `app/Console/Commands/EncerrarCampanhasExpiradas.php` | Encerramento de campanhas vencidas |
| `app/Console/Commands/GerarFaturasCartao.php` | Faturas mensais de cartão |
| `app/Console/Commands/SincronizarClassificacaoContabil.php` | Sync DRE/DFC no cadastro |
| `app/Console/Kernel.php` | Agendamento (quatro comandos + `dfe`) |

---

*Documento gerado com base na leitura do código. Novos Jobs ou comandos agendados devem ser acrescentados a este arquivo para manter o inventário único.*
