# Guia: criar um orçamento

Como um sistema externo cria um orçamento no GransoftWeb: abrir o documento, informar cliente e obra, condições da venda, incluir peças sob medida, produtos de revenda e serviços, aplicar desconto e gerar o PDF.

Pré-requisitos: `userHash` e `Servidor` ([Autenticação](../autenticacao.md)), usuário com permissão de criar orçamentos. **Teste com um usuário e clientes de teste**: cada chamada grava no banco na hora. Não existe um "salvar" final.

Os exemplos usam valores fictícios.

## 1. Visão geral

Todas as operações usam o mesmo endereço. O campo `Acao` diz o que fazer:

```
POST {Servidor}/API/AcoesDocumentoVendaMarmoraria
Content-Type: application/json

{ "Acao": 1, "userHash": "...", "Hash": "..." }
```

| Campo | Descrição |
|---|---|
| `Acao` | Número da operação (tabela no item 11). |
| `userHash` | Usuário autenticado. **Sempre no corpo**: este método ignora o header `Authorization`. |
| `Hash` | Identificador do **orçamento**. Vem na criação e vai em todas as chamadas seguintes. É diferente do `userHash`. |

Respostas: HTTP 200 sempre. Confira `Erro` antes de usar os dados ([Convenções](../convencoes.md)).

### Sequência

| Passo | O que fazer | Ação | Item |
|---|---|---|---|
| 1 | Localizar o cliente | `ObterLista` tipo 16 | 2 |
| 2 | Criar o orçamento e guardar o `Hash` | `2` | 3 |
| 3 | Completar cliente e obra (opcional) | `42`, `43` | 4 |
| 4 | Tabela de preço, pagamento, prazo, observação (opcional) | `3` | 5 |
| 5 | Incluir os itens | `19`, `39`, `40`, `20`, `21` | 6 e 7 |
| 6 | Desconto, frete e outros valores (opcional) | `50`, `51`, `3` | 8 |
| 7 | Conferir e gerar o PDF | `1`, `25`, `7` | 9 |
| 8 | Encerrar a edição | `37` | 10 |

O mínimo é: criar o orçamento (passo 2) e incluir os itens (passo 5).

**Ritmo:** cada item, medida e serviço é uma chamada, e o limite é de 50 por minuto. Use as medidas em lote (item 6.2) para economizar chamadas.

## 2. Localizar o cliente e os códigos

Use `ObterLista` ([Consultar clientes](consultar-clientes.md)):

| Para | `Tipo` |
|---|---|
| Cliente (com `Search` = texto) | `16` |
| Produto de produção: tipo de peça (pia, bancada, soleira…) | `5` |
| Material (pedra), com `Search` = `"1"` | `18` |
| Produto de revenda (cuba, torneira…) | `3` |
| Serviço | `6` |
| Acabamento (boleado, meia-esquadria…) | `7` |
| Tabela de preço / condição de pagamento / prazo de entrega | `8` / `1` / `11` |

Se houver mais de um registro compatível, pergunte ao usuário qual usar.

## 3. Criar o orçamento: ação 2

Cliente já cadastrado (código vindo do item 2):

```json
{
  "Acao": 2,
  "userHash": "aEJ6cG1oUjllOExOMXB2ay91TE5U...",
  "DadosCliente": { "Codigo": 1523, "Nome": "MARIA DE SOUZA" }
}
```

Cliente novo: envie `"Codigo": 0` e o nome. Os demais dados vão no item 4.

Resposta:

```json
{ "Acao": 2, "Erro": "", "Hash": "bVc5a1RqUnhWMGx3...", "Tipo": 0 }
```

**Guarde o `Hash`.** Para ver número e totais, use a ação `1` (item 9).

`Tipo` do documento: `0` orçamento (pode ser alterado), `1` venda finalizada/pedido (não pode ser alterado pela API).

## 4. Cliente e obra: ações 42 e 43

> **Atenção: estas ações substituem todos os campos de endereço.** Campo não enviado fica vazio. Para mudar só o telefone, consulte o documento (ação `1`), altere o campo no objeto `DadosCliente` recebido e envie o objeto inteiro.

```json
{
  "Acao": 42,
  "userHash": "aEJ6cG1oUjllOExOMXB2ay91TE5U...",
  "Hash": "bVc5a1RqUnhWMGx3...",
  "DadosCliente": {
    "Codigo": 0, "Nome": "Carlos Pereira",
    "Rua": "Av. Brasil", "Numero": "850", "EndCompl": "Apto 302", "Bairro": "Funcionários",
    "Cidade": "Belo Horizonte", "Estado": "MG", "CEP": "30140-001",
    "CpfCnpj": "98765432100", "InscRG": "", "Celular": "31988887777", "Telefone": "", "Email": "carlos@exemplo.com.br"
  }
}
```

Obra: ação `43` com `DadosObra` (`Nome`, `Rua`, `Numero`, `EndCompl`, `Bairro`, `Cidade`, `Estado`, `CEP`, `Contato`). Sem endereço de obra, o sistema usa o do cliente.

Estas ações gravam **só no orçamento**. Para gravar o cliente novo no cadastro, use a ação `56` (`SalvarCadastroCliente`) com o mesmo `DadosCliente`, apenas quando `CpfCnpj` estiver preenchido.

`Estado` é a sigla da UF. `CpfCnpj` só números.

## 5. Condições da venda: ação 3

```json
{
  "Acao": 3,
  "userHash": "aEJ6cG1oUjllOExOMXB2ay91TE5U...",
  "Hash": "bVc5a1RqUnhWMGx3...",
  "FieldsToUpdate": "Tabela,CondPagamento,PrazoEntrega,txtReferencia,Observacao",
  "Tabela": { "Codigo": 1 },
  "CondPagamento": { "Codigo": 3 },
  "PrazoEntrega": { "Codigo": 2 },
  "txtReferencia": "Cozinha e banheiros",
  "Observacao": "Medição final a confirmar na obra."
}
```

> **Sempre envie `FieldsToUpdate`**, com os nomes separados por vírgula. Se ele vier vazio, o servidor grava **todos** os campos do documento, incluindo os que você não enviou.

O vendedor é o do usuário autenticado e volta na consulta (ação `1`).

## 6. Peça sob medida (item de produção)

Uma peça sob medida (pia, bancada, lavatório, soleira) é um item com:

- um **produto de produção** (lista `5`): o tipo da peça;
- um **material** (lista `18`): a pedra;
- **medidas**: as partes da peça (tampo, frontão, saia…), com comprimento e largura em metros;
- **serviços e acabamentos** opcionais (item 7).

### 6.1 Criar o item: ação 19

```json
{
  "Acao": 19,
  "userHash": "aEJ6cG1oUjllOExOMXB2ay91TE5U...",
  "Hash": "bVc5a1RqUnhWMGx3...",
  "Itens": [
    {
      "CodProduto": 55,
      "TipoItem": 0,
      "Qtd": 1,
      "Medidas": [ { "Medida": "", "Material": { "CodMaterial": 120 } } ]
    }
  ]
}
```

- `TipoItem: 0` faz o servidor deduzir o tipo pelo cadastro do produto. É o recomendado.
- Deixe `Medida` vazia aqui: as medidas entram no passo seguinte.
- A resposta traz o item em `Itens[0]`, com o `Codigo`. **Guarde-o.**
- Se `Erro` vier pedindo as medidas **e** `Itens[0].Codigo` estiver preenchido, o item foi criado: siga para 6.2. Qualquer outro erro interrompe.

**Peças-modelo vazias.** Ao escolher a pedra, o sistema pode criar sozinho partes-modelo do produto (ex.: Tampo, Saia, Frontão) com área zero. Elas vêm em `Itens[0].Medidas` na resposta. **Anote os códigos delas** e remova as que continuarem com `QtdTotalM2` igual a `0` depois de lançar as medidas reais (6.3). Peças vazias impedem a geração do pedido.

### 6.2 Incluir as medidas: ação 39

**Opção A, campos separados (recomendada).** Uma chamada por parte. O servidor não interpreta texto, então não há risco de leitura errada:

```json
{
  "Acao": 39,
  "userHash": "aEJ6cG1oUjllOExOMXB2ay91TE5U...",
  "Hash": "bVc5a1RqUnhWMGx3...",
  "Itens": [
    {
      "Codigo": 9002,
      "Medidas": [
        {
          "Medida": "",
          "Descricao": "Tampo",
          "Qtd": 1,
          "Comp": 1.20,
          "Larg": 0.60,
          "CompCompl": 0,
          "LargCompl": 0,
          "Material": { "CodMaterial": 120 }
        }
      ]
    }
  ]
}
```

**Opção B, texto com várias partes de uma vez.** Economiza chamadas. Medidas em metros com vírgula decimal, partes separadas por `;`, quantidade opcional na frente:

```json
{
  "Acao": 39,
  "userHash": "aEJ6cG1oUjllOExOMXB2ay91TE5U...",
  "Hash": "bVc5a1RqUnhWMGx3...",
  "Itens": [
    {
      "Codigo": 9002,
      "Medidas": [
        {
          "Medida": "Tampo 1,20 x 0,60; Frontão 1,20 x 0,10; Saia 1,20 x 0,05; 2 x 0,90 x 0,40 Lateral",
          "Material": { "CodMaterial": 120 }
        }
      ]
    }
  ]
}
```

Resposta: o item com as medidas calculadas.

```json
{
  "Erro": "",
  "Itens": [
    {
      "Codigo": 9002,
      "QtdM2": 0.912,
      "vrTotal": 820.80,
      "Medidas": [
        { "Codigo": 7001, "Descricao": "Tampo", "Qtd": 1, "Comp": 1.2, "Larg": 0.6, "QtdTotalM2": 0.72 },
        { "Codigo": 7002, "Descricao": "Frontão", "Qtd": 1, "Comp": 1.2, "Larg": 0.1, "QtdTotalM2": 0.12 }
      ]
    }
  ]
}
```

**Confira `Comp`, `Larg` e `Qtd` de cada medida na resposta.** Medida errada: corrija com a ação `41` (`EditarMedidas`, com o `Codigo` da medida) ou remova com a `40`.

Frontão e saia são partes separadas do tampo. Não invente medidas que o usuário não informou.

### 6.3 Remover as peças-modelo vazias: ação 40

Para cada peça-modelo anotada em 6.1 que ainda tenha `QtdTotalM2` igual a `0`:

```json
{
  "Acao": 40,
  "userHash": "aEJ6cG1oUjllOExOMXB2ay91TE5U...",
  "Hash": "bVc5a1RqUnhWMGx3...",
  "Itens": [ { "Codigo": 9002, "Medidas": [ { "Codigo": 7000 } ] } ]
}
```

## 7. Acabamentos, serviços e revenda

### 7.1 Na peça: ação 20

Acabamento, serviço ou produto de revenda **ligado a uma peça** (ex.: boleado na pia, cuba na bancada):

```json
{
  "Acao": 20,
  "userHash": "aEJ6cG1oUjllOExOMXB2ay91TE5U...",
  "Hash": "bVc5a1RqUnhWMGx3...",
  "Itens": [ { "Codigo": 9002, "CodProduto": 77, "Qtd": 2.4 } ]
}
```

- `Codigo` é o item de produção. `CodProduto` é o acabamento, serviço ou produto.
- **Acabamentos de borda são em metros lineares** e o sistema não calcula a quantidade: some você as bordas que recebem o acabamento (ex.: frente de 1,20 m + duas laterais de 0,60 m = 2,40 ML).
- A resposta traz `Itens[0].Servicos`, cada um com `Codigo`. Para mudar a quantidade depois: ação `22` com `Servicos: [{ "Codigo": 501, "Qtd": 3, "FieldsToUpdate": "Qtd" }]`. Para remover: ação `24`.

### 7.2 Item avulso de revenda ou serviço: ações 19 e 21

```json
{ "Acao": 19, "userHash": "...", "Hash": "...", "Itens": [ { "CodProduto": 310, "TipoItem": 0 } ] }
```

Depois, quantidade e preço com a ação `21`. **`FieldsToUpdate` fica dentro do item:**

```json
{
  "Acao": 21,
  "userHash": "...",
  "Hash": "...",
  "Itens": [ { "Codigo": 9001, "Qtd": 2, "FieldsToUpdate": "Qtd" } ]
}
```

Se `IsQtdDecimal` vier `false` na criação, envie quantidade inteira. O preço unitário vem da tabela de preço. Só envie `vrUnit` (e inclua em `FieldsToUpdate`) se a regra da empresa permitir alterar preço.

## 8. Desconto, frete e outros valores

Informe **depois de incluir todos os itens**: o desconto depende do total.

Desconto em percentual, ação `50`:

```json
{ "Acao": 50, "userHash": "...", "Hash": "...", "Detalhe": { "PercDesconto": 5 } }
```

Desconto em reais, ação `51`:

```json
{ "Acao": 51, "userHash": "...", "Hash": "...", "Detalhe": { "ValorDesconto": 74.40 } }
```

O desconto passa pelas regras de limite do usuário. Confira em `Detalhe` da resposta o valor realmente aplicado.

Frete e outros valores, ação `3`:

```json
{
  "Acao": 3, "userHash": "...", "Hash": "...",
  "FieldsToUpdate": "ValorFrete,ValorOutros",
  "Detalhe": { "ValorFrete": 150.00, "ValorOutros": 0 }
}
```

Total do documento: `ValorTotalItens + ValorOutros + ValorFrete − ValorDesconto`.

## 9. Conferir e gerar o PDF

- **Ação `1`** (`ConsultarDocumento`): número (`NroDocumento`), cliente, obra, vendedor, tabela, condição de pagamento e `Detalhe` com os totais.
- **Ação `25`** (`ListarItens`): lista dos itens em `Itens`.
- **Ação `7`** (`VisualizarRelatorio`): gera o PDF. O link vem em `Anexos[0].Link`. Um segundo anexo, quando existe, é o relatório com os desenhos.

Confira pelo menos: cliente, quantidade de itens, `Detalhe.ValorTotalItens` e `Detalhe.ValorTotalDocumento`.

## 10. Encerrar a edição: ação 37

```json
{ "Acao": 37, "userHash": "...", "Hash": "..." }
```

Libera o documento no servidor. Chame ao terminar ou antes de abrir outro orçamento.

## 11. Referência das ações

Todas: `POST {Servidor}/API/AcoesDocumentoVendaMarmoraria`, com `Acao` e `userHash`. Exceto `2`, `31` a `33`, `44`, `45` e `46`, todas exigem o `Hash` do documento.

| `Acao` | Nome | Envia além de `Acao`/`userHash`/`Hash` |
|---|---|---|
| 1 | ConsultarDocumento | — |
| 2 | CriarDocumento | `DadosCliente` (sem `Hash`) |
| 3 | SalvarDocumento | `FieldsToUpdate` e os campos listados |
| 4 | ExcluirDocumento | — (só orçamento; exige permissão de exclusão) |
| 5 | RevisaoDocumento | — |
| 6 | CopiarDocumento | — (novo orçamento a partir de outro documento) |
| 7 | VisualizarRelatorio | — (PDF em `Anexos`) |
| 10 | CarregarImagem | `Anexos: [{ Nome, Array }]` (bytes do arquivo); com `Itens[0].Codigo` + `ArquivoProjeto` = desenho do item |
| 11 | ExcluirImagem | `Anexos: [{ Nome }]` |
| 13 | ObterAnexos | — |
| 19 | CriarItem | `Itens[0]`: `CodProduto`, `TipoItem`, `Medidas[0].Material.CodMaterial` |
| 20 | CriarServico | `Itens[0]`: `Codigo` (item), `CodProduto`, `Qtd` |
| 21 | SalvarItem | `Itens[0]`: `Codigo`, campos e `FieldsToUpdate` |
| 22 | SalvarServico | `Itens[0].Codigo`, `Servicos[0]`: `Codigo`, `Qtd`, `FieldsToUpdate` |
| 23 | ExcluirItem | `Itens[0].Codigo` |
| 24 | ExcluirServico | `Itens[0].Codigo`, `Servicos[0].Codigo` |
| 25 | ListarItens | — |
| 31 / 32 / 33 | LocalizarOrcamento / LocalizarPedido / LocalizarOrcamentoPedido | `Localiza` (número ou nome) — sem `Hash` |
| 37 | RemoveCache | — |
| 38 | DetalheItemVenda | `Itens[0].Codigo` |
| 39 | AdicionarMedidas | `Itens[0].Codigo`, `Medidas[0]` |
| 40 | ExcluirMedidas | `Itens[0].Codigo`, `Medidas[0].Codigo` |
| 41 | EditarMedidas | `Itens[0].Codigo`, `Medidas[0]` com `Codigo` |
| 42 | AlteraDadosCliente | `DadosCliente` completo |
| 43 | AlteraDadosObra | `DadosObra` completo |
| 44 | ObterDadosCliente | `DadosCliente.Codigo` — sem `Hash` |
| 45 / 46 | UltimosOrcamentos / UltimosPedidos | — sem `Hash` |
| 50 / 51 | AlterarPercDesconto / AlterarValorDesconto | `Detalhe.PercDesconto` / `Detalhe.ValorDesconto` |
| 53 | AlterarMaterial | `Itens[0].Codigo`, `Medidas[0].Material.CodMaterial` |
| 54 | CopiarItem | `Itens[0].Codigo` |
| 56 | SalvarCadastroCliente | `DadosCliente` |
| 57 | ExportarXml | — (XML do documento, como o "Exportar para arquivo") |
| 58 | AnaliseRentabilidade | — (exige permissão de ver custo) |

As listagens (`31` a `33`, `45`, `46`) devolvem `Documentos`, cada um com `Hash`, `Tipo`, `NroDocumento`, `ClienteNome` e `ValorTotalDocumento`. Use o `Hash` para abrir o documento com a ação `1`.

## 12. Erros comuns

| Sintoma | Causa | Solução |
|---|---|---|
| `Erro` em todas as chamadas | `userHash` no header, ou `Hash` do orçamento trocado pelo `userHash`. | `userHash` no corpo; `Hash` = o devolvido na criação. |
| Erro ao alterar documento com `Tipo` igual a `1` | Pedido não pode ser alterado. | Gere um orçamento a partir dele (ação `6`). |
| Endereço do cliente apagado | Ação `42`/`43` com objeto incompleto. | Envie o objeto inteiro (item 4). |
| Campos do documento alterados sem querer | Ação `3` sem `FieldsToUpdate`. | Sempre envie `FieldsToUpdate`. |
| Pedido não é gerado na tela | Peças-modelo com área zero. | Remova-as (item 6.3). |
| Medida com dimensões erradas | Texto interpretado de outra forma. | Use a opção A (campos separados) e confira a resposta. |
| "Limite de requisições excedido" | Mais de 50 chamadas por minuto. | Espere e repita; use medidas em lote. |

Exemplo completo: [kit/exemplo-orcamento.js](../kit/exemplo-orcamento.js).
