# Documentação técnica — Contratos e recorrência (assinaturas / cobranças periódicas)

Este documento descreve o cadastro de **contratos**, **itens** e **veículos** vinculados, a **recorrência** de cobrança, a geração de **NF** via `ContratoService`/`NfService` e o comando Artisan **`assinatura:gerar-cobrancas`**. A visão financeira (parcelas, saldo, diferenças de dia útil) está em **`Doc_Modulo_Financeiro.md`** (seções 1.8, 1.9 e 3.9). **Agendamento, fila de pontuação e demais comandos** estão centralizados em **`Doc_Jobs_e_Agendamento.md`**.

---

## 1. Visão de negócio

| Conceito | Detalhe no código |
|----------|-------------------|
| **Modalidade** | `modalidade = CONTRATO` → contrato comercial (locação, serviço recorrente com cliente/fornecedor). `modalidade = RECORRENCIA` → assinatura SaaS / cobrança automática do tenant (ver **`Doc_Modulo_Assinatura_Contrato.md`**). O CRUD **`admin/contratos`** lista e edita **somente** `CONTRATO`. |
| **Tipo financeiro** | `tipo = C` → receita (NF com `saida = V`); `tipo = D` → despesa/compra (`saida = N` na NF gerada pelo `NfService`). |
| **Partes** | `idempresa`, `idpessoa` (cliente/fornecedor do cadastro `pessoa`). |
| **Valores** | `valorcontrato`, `valoracordocontrato` (priorizado no cálculo da NF), `descontocontrato`, `totalcontrato`. |
| **Classificação fiscal/contábil** | `idprodservtipo` opcional (`TipoProdutoServico`). |
| **Pagamento** | `idformapagamento`, `idagencia`, `diavencimento` (dia do mês, 1–31), `antecedencia` (dias antes do vencimento em que a cobrança “pode” ser gerada). |
| **Recorrência** | `recorrencia`: `semanal`, `mensal`, `trimestral`, `semestral`, `anual`; janela `datainiciorecorrencia` / `datafimrecorrencia`; `dataultimarecorrencia` atualizada após cada faturamento. |
| **Status** | `PENDENTE`, `ATIVO`, `INATIVO`. Listagem padrão (`index`, `fetch`) exclui `INATIVO`. |
| **Documento / endereço** | Flags `mostracontrato`, `mostraendereco`; endereço pode ser preenchido no contrato (`cep`, `endereco`, etc.) independentemente da `pessoa`. |

```mermaid
flowchart LR
  subgraph cadastro
    CT[Contrato]
    CI[ContratoItem]
    CV[ContratoVeiculo]
    CT --> CI
    CT --> CV
  end
  subgraph geracao
    CS[ContratoService]
    NS[NfService]
    CT --> CS
    CS --> NS
    NS --> NF[Nf + NfItem + NfParcela]
  end
```

---

## 2. Rotas administrativas (`routes/web.php`, prefixo `admin`)

| Método / rota | Nome | Ação |
|---------------|------|------|
| `GET` `contratos` | `admin.contratos.index` | Lista paginada (100), **`modalidade = CONTRATO`**, `status != INATIVO` (salvo filtro explícito). |
| `GET` `contratos/procurar` | `admin.contratos.search` | Filtro por intervalo em `datafim`, texto e status; grava `date_range` na sessão por segmento da URL. |
| `GET` `contratos/fetch` | `admin.contratos.fetch` | JSON paginado para grid (colunas ID, empresa, tipo, nome, cliente, endereço resumido, status, prazo). |
| `GET/POST` … | `admin.contratos.*` | Resource CRUD (`create`, `store`, `edit`, `update`, `destroy`). |
| `POST` `contratos/{contrato}/atualizar-nfs-em-andamento` | `contratos.atualizar-nfs-em-andamento` | Propaga valores/forma/agência às NFs com parcelas não quitadas. |
| `DELETE` `contratoveiculo/{idcontratoveiculo}` | `contratoveiculo.removeveiculo` | Remove vínculo veículo–contrato (JSON). |
| `POST` `contratoveiculoadd/{idcontrato}` | `contratoveiculo.addveiculo` | Cria linha vazia em `contratoveiculo`. |
| `DELETE` `contratoitem/{idcontratoitem}` | `contratoitem.removeitem` | Remove item do contrato (JSON). |
| `POST` `contratoitemadd/{idcontrato}` | `contratoitem.additem` | Cria linha vazia em `contratoitem`. |

**Middleware:** `contratos.search` exige role `ADMIN`; demais rotas do grupo seguem o arquivo `web.php` do projeto.

---

## 3. `ContratoController`

### 3.1 `index` e sessão de datas

- Se existir `clear` na query ou sessão vazia para `{segmento}_dateRange`, limpa a chave.
- Se a sessão já tiver intervalo, redireciona para `search` com `date_range`.
- Caso contrário, lista contratos com `empresa` e `pessoa`, escopo **`modalidadeContrato()`**, ordenação `datafim` desc.
- **`store`:** força `modalidade = CONTRATO` e `assinatura_saas = false` (hidden no `_form`).
- **`edit` / `update` / `destroy`:** `resolverContratoComercial()` retorna 404 se o registro for `RECORRENCIA` (assinaturas não são editadas por esta tela).

### 3.2 `store` — validação

Campos validados explicitamente:

- `idempresa` (obrigatório, existe em `empresa`),
- `idpessoa` (obrigatório, existe em `pessoa`),
- `nome`, `tipo` (`D` ou `C`),
- `idprodservtipo` (opcional),
- `recorrencia` (`semanal`, `mensal`, `trimestral`, `semestral`, `anual`),
- `diavencimento` (string, tamanho 1–31),
- `antecedencia` (opcional),
- `status` (`PENDENTE`, `ATIVO`, `INATIVO`).

Regra manual: se `datainiciorecorrencia` > `datafimrecorrencia`, retorna erro. Checkboxes `mostracontrato`, `mostraendereco`, `mostraveiculos` normalizados para boolean (`on` → true). Valores monetários (`descontocontrato`, `totalcontrato`, `valorcontrato`, `valoracordocontrato`) convertidos do formato BR. `idprodservtipo` vazio vira `null`. **`Model::create($data)`** — apenas atributos em `$fillable` do `Contrato` são persistidos; `mostraveiculos` não está em `$fillable` (pode ser ignorado pelo Eloquent se vier só no request).

### 3.3 `edit`

Carrega contrato, listas de pessoas (`pessoapadrao = false`), empresas ativas, tipos de produto/serviço (`padrao = false`), itens do contrato, veículos da empresa, vínculos `contratoveiculo`, produtos filtrados por `tipo` do contrato (`eh_compravel` vs `eh_vendivel`), formas de pagamento e agências.

### 3.4 `update`

- Transação DB.
- Com `redirect !== false` (padrão): normaliza monetários e checkboxes `mostracontrato`, `mostraendereco`; atualiza linhas de `ContratoVeiculo` e `ContratoItem` enviadas em massa (`idcontratoveiculo` / `idcontratoitem`); novos itens sem `idcontratoitem` válido são criados se houver `idprodserv` na linha.
- **`$contrato->update($contratoUpdate)`** com todo o payload (atenção a campos extras).
- Se `datainiciorecorrencia`, `recorrencia` e `diavencimento` estiverem preenchidos: laço **até 24 vezes** enquanto `getProximaDataRecorrencia()` for verdadeiro, chamando `ContratoService::processarRecorrencias` (o serviço recarrega/atualiza `dataultimarecorrencia` a cada NF gerada).
- `redirect === false`: commit e retorno JSON com `status` do contrato (uso típico AJAX).
- Após commit normal: se existir NF com `idcontrato` e alguma parcela com `status != QUITADO`, flash para exibir modal sugerindo **`atualizarNfsEmAndamento`**.

### 3.5 `atualizarNfsEmAndamento`

Recalcula `totalnf` / `valornf` / `descontonf` a partir do contrato, atualiza NFs elegíveis e, para cada parcela não quitada, valor, forma, agência e datas de vencimento alinhadas ao `diavencimento` (respeitando último dia do mês).

### 3.6 `search`

- `date_range` no formato `d/m/y - d/m/y` filtra `datafim` entre as datas.
- `search` busca em `nome`, empresa (`nomefantasia`/`razaosocial`) e pessoa (`nome`/`razaosocial`).
- `status` aplicado com `LIKE` (valor vindo do request).

### 3.7 `fetch` (detalhe de implementação)

Monta colunas para frontend; no partial de status usa `'id' => $contrato->idnf` — o model **Contrato** não define `idnf`; em runtime isso tende a `null` (possível inconsistência visual no indicador de status).

### 3.8 `additem` / `removeitem` / `addveiculo` / `removeveiculo`

- `additem` / `addveiculo`: criam registro mínimo (`idcontrato`, `idempresa`) e retornam JSON com o novo id.
- `removeitem` / `removeveiculo`: o parâmetro de rota é **`idcontratoitem`** / **`idcontratoveiculo`**, embora o nome do argumento do método seja `idContrato` em `removeitem` (usa `ContratoItem::find($idContrato)` — o valor recebido é o id da linha, não do contrato).

---

## 4. Models e tabelas

### 4.1 `Contrato` (`contrato`)

- **Escopo:** `TenantScope`.
- **PK:** `idcontrato`.
- **Timestamps:** `criadoem`, `alteradoem`.
- **Fillable:** vide `App\Models\Contrato` — inclui vínculos, datas de contrato e de recorrência, valores, endereço, status, flags de exibição, auditoria `criadopor`/`alteradopor`.
- **Relações:** `empresa()`, `pessoa()`, `prodservtipo()`, `nfs()` → `Nf`.

**`verificaRecorrencia()`:** exige `recorrencia`, `datainiciorecorrencia` e `diavencimento` não vazios.

**`getProximaDataRecorrencia(): ?Carbon`**

- Fora do intervalo `[datainiciorecorrencia, datafimrecorrencia]` (quando fim definido) → `null`.
- **Semanal:** primeiro vencimento = início da cobrança; depois, última recorrência + 7 dias. Janela de emissão: `vencimento - antecedencia` dias até hoje.
- **Mensal / trimestral / semestral / anual:** incremento em meses (1, 3, 6, 12) a partir de `dataultimarecorrencia` ou primeiro vencimento no mês de início ajustado ao `diavencimento` (e ao `daysInMonth`). Mesma regra de `abertura` com `antecedencia`.
- **`precisaGerarCobranca()`:** delega para `getProximaDataRecorrencia()`.

O método protegido **`calcularProximoVencimento`** existe no model mas **não** é usado pelo fluxo de `getProximaDataRecorrencia` / `ContratoService` analisado aqui.

### 4.2 `ContratoItem` (`contratoitem`)

- Escopo `TenantScope`; PK `idcontratoitem`.
- Campos: `idcontrato`, `idempresa`, `idprodserv`, `iditem`, `item`, `idgrupoitem`, `idtipoitem`, `qtd`, `un`, `valorun`, `valoritem`.
- Relações: `prodserv()`, `grupoitem()`.
- **Observação:** `contrato()` no model aponta para `Nf` com chave `idcontrato` — inconsistente com a tabela `contrato`; o vínculo real de negócio é `idcontrato` → `Contrato`.

### 4.3 `ContratoVeiculo` (`contratoveiculo`)

- Escopo `TenantScope`; PK `idcontratoveiculo`.
- Campos: `idcontrato`, `idveiculo`, `idempresa`, auditoria.
- Relações: `contrato()`, `veiculo()`, `empresa()`.

Documentação de veículos: **`Doc_Modulo_Veiculo.md`**.

---

## 5. Serviços

### 5.1 `ContratoService::processarRecorrencias`

Orquestração descrita resumidamente em **`Doc_Modulo_Financeiro.md`**: loop interno (máx. 32), `getProximaDataRecorrencia`, `NfService::store`, `handleParcels`, `storeNfItem`, atualização de `dataultimarecorrencia`. Em falha, log e exceção propagada (o comando Artisan faz rollback por contrato).

### 5.2 `NfService`

Criação da NF e parcelas/itens a partir do contrato — ver **`Doc_Modulo_Financeiro.md`** § 1.8.

---

## 6. Comando Artisan — cobrança agendada

| Assinatura | Descrição |
|------------|-----------|
| `php artisan assinatura:gerar-cobrancas` | Processa **todos** os contratos com `status = ATIVO`. |
| `--debug` | Lista, por contrato, se gerará cobrança e o motivo quando não gerar. |

Fluxo: para cada contrato ativo, se `getProximaDataRecorrencia()` retornar data, abre transação e chama `ContratoService::processarRecorrencias`. Diferente do **`ContratoController::update`**, aqui **não** há o laço externo de 24 iterações — uma chamada ao serviço por contrato (o serviço interno ainda pode gerar múltiplas NFs em sequência até esgotar vencimentos elegíveis).

**Agendamento:** ver **`Doc_Jobs_e_Agendamento.md`** § 3 e § 5 (`assinatura:gerar-cobrancas` a cada minuto no `Kernel`).

O comando processa contratos **comerciais e de assinatura** (`CONTRATO` e `RECORRENCIA`) desde que estejam `ATIVO` e dentro da janela de recorrência.

### 6.1 Coluna “Origem” no financeiro

Quando a NF possui `idcontrato`, `NfParcela::tipoOrigemParaNf()` consulta `contrato.modalidade`:

| `modalidade` | Badge | Sigla | Telas |
|--------------|-------|-------|--------|
| `CONTRATO` | Contrato (azul) | CTO | Vendas, compras, contas a pagar/receber, extrato |
| `RECORRENCIA` | Recorrência (info) | REC | Idem (assinatura SaaS provisionada pelo tenant) |

Implementação: `app/Models/NfParcela.php` (`definicoesTipoOrigem`, `badgeOrigemHtml`, `badgeOrigemSiglaHtml`). Detalhes em **`Doc_Modulo_Financeiro.md`** § 1.11.

---

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

| Arquivo | Função |
|---------|--------|
| `app/Http/Controllers/Admin/ContratoController.php` | CRUD, busca, itens/veículos, recorrência no `update`, atualização de NFs em andamento. |
| `app/Models/Contrato.php` | Dados, `modalidade`, scopes `modalidadeContrato` / `modalidadeRecorrencia`, `getProximaDataRecorrencia`. |
| `app/Models/ContratoItem.php` / `ContratoVeiculo.php` | Linhas do contrato. |
| `app/Services/ContratoService.php` | `processarRecorrencias`. |
| `app/Services/NfService.php` | NF + parcelas + itens originados do contrato. |
| `app/Console/Commands/GerarCobrancaAssinatura.php` | Job manual/agendado de cobranças. |
| `resources/views/admin/contratos/` | Formulários e listagens. |

---

*Documento gerado com base na leitura do código. Migrações e constraints de FK devem ser confirmadas no banco de produção.*
