Pular para conteúdo

Venda Casada - Beneficiamento com Vinculação de Pedido de Venda - NH-168

Data de Implementação: 2025-12-01 Jira Issue: NH-168 Módulo: Comercial Tela: Adicionar/Editar Pedido (Cotação) Tipo: Feature - Nova Funcionalidade


Índice

  1. Resumo
  2. Contexto e Motivação
  3. Conceito de Venda Casada
  4. Comportamento Implementado
  5. Fluxo de Utilização
  6. Detalhes Técnicos
  7. Estrutura de Dados
  8. Validações e Regras de Negócio
  9. Restrições de Ações
  10. Como Testar
  11. Troubleshooting
  12. Impacto no Sistema
  13. Arquivos Modificados
  14. Referências

Resumo

Implementação da funcionalidade de Venda Casada que permite vincular itens de um Pedido de Venda existente a um Pedido de Beneficiamento. Esta funcionalidade é utilizada quando a empresa realiza um serviço de beneficiamento (ex: corte, usinagem) em material que será posteriormente entregue ao cliente final de uma venda já existente.

Natureza de Operação: "Remessa Beneficiamento para posterior retorno com Venda Casada" (TipoNaturezaOperacaoEnum.BeneficiamentoVendaCasada)

Comportamento Principal: - Quando a natureza de operação é "BeneficiamentoVendaCasada", a interface de inclusão de itens muda - Os botões "Novo Item de Estoque" e "Novo Item de Terceiro" são ocultados - Uma nova seção "Vincular Item de Pedido de Venda (Venda Casada)" é exibida - O usuário busca e seleciona itens de Pedidos de Venda existentes para vincular ao pedido de beneficiamento


Contexto e Motivação

O que é Venda Casada?

A Venda Casada é um processo comercial onde:

  1. Primeiro: Um cliente faz um pedido de compra de material (Pedido de Venda)
  2. Segundo: A empresa envia o material para um fornecedor realizar beneficiamento (corte, usinagem, etc.)
  3. Terceiro: O material beneficiado retorna e é entregue ao cliente final

Neste fluxo, é essencial manter a rastreabilidade entre: - O Pedido de Venda original (cliente final) - O Pedido de Beneficiamento (envio para fornecedor)

Problema Anterior

Antes desta implementação: - Não havia forma de vincular o pedido de beneficiamento ao pedido de venda original - A rastreabilidade era manual, sujeita a erros - Relatórios gerenciais não conseguiam cruzar informações automaticamente

Benefícios da Implementação

  • Rastreabilidade automática: Vínculo direto entre pedidos
  • Integridade de dados: Referências em banco de dados
  • Controle de itens: Itens vinculados têm ações restritas
  • Relatórios integrados: Possibilidade de consultas cruzadas

Conceito de Venda Casada

Diagrama do Fluxo

┌─────────────────────────────────────────────────────────────────────────────┐
│                           FLUXO DE VENDA CASADA                             │
└─────────────────────────────────────────────────────────────────────────────┘

    ┌──────────────────┐                    ┌──────────────────┐
    │  PEDIDO DE VENDA │                    │   PEDIDO DE      │
    │     (Original)   │                    │  BENEFICIAMENTO  │
    │                  │                    │                  │
    │  Natureza:       │                    │  Natureza:       │
    │  "Vendas de      │◄───── VÍNCULO ─────│  "Remessa        │
    │   Produto"       │                    │   Beneficiamento │
    │                  │                    │   Venda Casada"  │
    │  Cliente: ABC    │                    │                  │
    │  Item: Barra     │                    │  Destinatário:   │
    │  Alumínio 10kg   │                    │  Fornecedor XYZ  │
    └────────┬─────────┘                    └────────┬─────────┘
             │                                       │
             │                                       │
             ▼                                       ▼
    ┌──────────────────┐                    ┌──────────────────┐
    │   Item Pedido    │                    │   Item Pedido    │
    │   de Venda       │                    │  Beneficiamento  │
    │                  │                    │                  │
    │  PedidoItem.Id=4 │◄─── REFERÊNCIA ────│ PedidoVenda-     │
    │                  │     DIRETA         │ VinculadoId=15   │
    │  Mercadoria:     │                    │                  │
    │  LTBC001         │                    │ PedidoItemVenda- │
    │                  │                    │ VinculadoId=4    │
    └──────────────────┘                    └──────────────────┘

                    ┌──────────────────────────────┐
                    │      BENEFICIAMENTO          │
                    │                              │
                    │  Material enviado para       │
                    │  FORNECEDOR realizar:        │
                    │  - Corte                     │
                    │  - Usinagem                  │
                    │  - Tratamento                │
                    │                              │
                    │  Após beneficiamento:        │
                    │  Material retorna para       │
                    │  entrega ao CLIENTE FINAL    │
                    └──────────────────────────────┘

Entidades Envolvidas

Entidade Papel Exemplo
Cliente Comprador final do material Empresa ABC Ltda
Fornecedor Prestador do serviço de beneficiamento Usinagem XYZ
Pedido de Venda Pedido do cliente para compra do material Pedido #15
Pedido de Beneficiamento Pedido de envio ao fornecedor para beneficiamento Pedido #53
Mercadoria Material sendo vendido/beneficiado LTBC001 - Barra Chata Latão

Tipo de Item: Sempre Estoque (Não Terceiros)

IMPORTANTE: Itens de Venda Casada são sempre itens de estoque, nunca itens de terceiros (Ordem de Compra).

Esta distinção é fundamental porque:

  1. Fluxo de Material: O material que vai para beneficiamento é mercadoria própria da empresa (estoque), não material comprado de terceiros para revenda
  2. Origem no SGE 2.0: No sistema legado, a operação REM.BENEF (Remessa Beneficiamento para Venda Casada) é tratada junto com operações de estoque como TRANSF.V (Transferência de Venda), usando a grid gvItensTransferencia
  3. Unidade de Medida: O SGE 2.0 configura explicitamente UnidadePergunta = "KG" para itens de Venda Casada, característico de itens de estoque

Evidência do Código SGE 2.0

// frm_Pedidos.aspx.cs - Linha 5190
if (ddlOperacao.SelectedValue.ToString().ToUpper() == "TRANSF.V" ||
    ddlOperacao.SelectedValue.ToString().ToUpper() == "REM.BENEF" ||  // Venda Casada
    ddlOperacao.SelectedValue.ToString().ToUpper() == "ORCA 2")
{
    gvItensTransferencia.Visible = true;  // Grid de itens de ESTOQUE
    gvItens.Visible = false;
    // ...
}

// frm_Pedidos.aspx.cs - Linha 7058
if (ddlOperacao.SelectedValue.ToString().ToUpper().Equals("REM.BENEF"))
{
    ViewState["UnidadePergunta"] = "KG";  // Unidade de ESTOQUE
    ddlUnidadePreco.Items.Clear();
    AddEmptyItem(ddlUnidadePreco, "KG", "KG");
    // ...
}

Implicação Técnica

No SGE 3.0, a view de visualização de itens de Venda Casada (View) utiliza apenas ItemEstoque/View.cshtml, nunca ItemTerceiro/View.cshtml:

// PedidoItemController.cs
[Route("visualizar-item/{pedidoId:int}/{pedidoItemId:int}")]
public async Task<IActionResult> View(int pedidoId, int pedidoItemId)
{
    // ...
    // NH-168: Venda Casada é sempre item de estoque (não de terceiros/Ordem de Compra)
    return View("ItemEstoque/View", pedidoItemViewModel);
}

Comportamento Implementado

Cenário: Natureza de Operação "BeneficiamentoVendaCasada"

Quando o usuário seleciona a natureza de operação "Remessa Beneficiamento para posterior retorno com Venda Casada":

1. Interface de Inclusão de Itens

Elementos Ocultados: - Botão "Novo Item de Estoque" - Botão "Novo Item de Terceiro"

Elementos Exibidos: - Seção "Vincular Item de Pedido de Venda (Venda Casada)"

┌─────────────────────────────────────────────────────────────────────────────┐
│  SEÇÃO: ITENS                                                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  Vincular Item de Pedido de Venda (Venda Casada)                    │   │
│  ├─────────────────────────────────────────────────────────────────────┤   │
│  │                                                                      │   │
│  │  Nº Pedido de Venda    Pedido de Venda         Item do Pedido       │   │
│  │  ┌─────────┬───┐      ┌──────────────▼┐       ┌──────────────▼┐ [+] │   │
│  │  │ 15      │ 🔍│      │ Pedido #15    │       │ Item 4 - ...  │     │   │
│  │  └─────────┴───┘      └───────────────┘       └───────────────┘     │   │
│  │                                                                      │   │
│  │  ┌─────────────────────────────────────────────────────────────┐    │   │
│  │  │ Mercadoria: LTBC001 - BARRA CHATA LATAO                     │    │   │
│  │  │ Quantidade: 1,000 PC | Vr. Unitário: R$ 50,00               │    │   │
│  │  │ Vr. Total: R$ 50,00                                          │    │   │
│  │  └─────────────────────────────────────────────────────────────┘    │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  TABELA DE ITENS                                                    │   │
│  ├──────┬────────┬────────────────────┬───────────┬────────┬──────────┤   │
│  │ Tipo │ Código │ Descrição          │ Qtde Sep. │ Valor  │  Ação    │   │
│  ├──────┼────────┼────────────────────┼───────────┼────────┼──────────┤   │
│  │ Est. │ LTBC001│ BARRA CHATA LATAO  │   1,000   │  50,00 │   [🗑]   │   │
│  │      │        │ (Venda Casada)     │           │        │          │   │
│  └──────┴────────┴────────────────────┴───────────┴────────┴──────────┘   │
│                                                                             │
│  * Itens de Venda Casada só podem ser EXCLUÍDOS (não editados)             │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

2. Restrições em Itens de Venda Casada

Itens vinculados a um Pedido de Venda (Venda Casada) possuem ações restritas:

Ação Item Normal Item Venda Casada
Editar ✅ Permitido ❌ Bloqueado
Excluir ✅ Permitido ✅ Permitido
Expedição ✅ Permitido ❌ Bloqueado

Motivo: Os dados do item vêm do Pedido de Venda original e não devem ser alterados individualmente.


Fluxo de Utilização

Passo a Passo Completo

Pré-requisitos

  1. Ter um Pedido de Venda existente:
  2. Status: "A Faturar" ou "Fechado"
  3. Natureza de Operação: Tipo "Venda"
  4. Com pelo menos um item

  5. O Destinatário do Pedido de Beneficiamento deve ser um Fornecedor cadastrado

Passo 1: Criar/Editar Pedido de Beneficiamento

  1. Acesse: Comercial > Adicionar Pedido ou Editar Pedido
  2. Selecione a Empresa de Emissão
  3. Selecione a Natureza de Operação: "Remessa Beneficiamento para posterior retorno com Venda Casada"

Passo 2: Configurar Destinatário (Fornecedor)

  1. Expanda a seção "Destinatário"
  2. Busque e selecione um Fornecedor cadastrado
  3. O fornecedor é quem realizará o serviço de beneficiamento

⚠️ IMPORTANTE: A natureza de operação exige que o destinatário seja um Fornecedor. Se o destinatário não estiver cadastrado como Fornecedor, o sistema retornará erro.

Passo 3: Vincular Item de Pedido de Venda

  1. Expanda a seção "Itens"
  2. Na seção "Vincular Item de Pedido de Venda (Venda Casada)":
  3. Digite o número do Pedido de Venda no campo "Nº Pedido de Venda"
  4. Clique no botão de busca (🔍)
  5. Selecione o Pedido de Venda no dropdown "Pedido de Venda"
  6. Selecione o Item no dropdown "Item do Pedido"
  7. Verifique as informações exibidas (Mercadoria, Quantidade, Valores)
  8. Clique no botão "+" para adicionar

Passo 4: Verificar Item Adicionado

  1. O item aparecerá na tabela de itens
  2. O campo "Observações" do item conterá: "Venda Casada - Pedido de Venda: X, Item: Y"
  3. O item terá apenas o botão de Excluir (🗑) disponível

Passo 5: Gravar Pedido

  1. Clique em "Gravar" para salvar o pedido
  2. O vínculo com o Pedido de Venda é persistido no banco de dados

Detalhes Técnicos

Endpoints da API

1. Buscar Pedidos de Venda para Venda Casada

URL: GET /comercial/pedido/buscar-pedidos-venda-venda-casada

Parâmetros: - numeroPedido (string, opcional): Filtro por número do pedido - destinatarioId (int, opcional): Filtro por destinatário

Resposta:

[
  {
    "id": 15,
    "numero": 15,
    "dataEmissao": "2025-11-30",
    "destinatarioNome": "Cliente ABC Ltda",
    "naturezaOperacaoNome": "Vendas de Produto",
    "situacao": "A Faturar"
  }
]

Filtros aplicados automaticamente: - TipoPedido = Pedido (não orçamento) - NaturezaOperacao.TipoNaturezaOperacao = Venda - TipoSituacaoPedido = AFaturar (3) ou Fechado (4)

2. Buscar Itens do Pedido de Venda

URL: GET /comercial/pedido/buscar-itens-pedido-venda-casada

Parâmetros: - pedidoVendaId (int, obrigatório): ID do pedido de venda

Resposta:

[
  {
    "id": 4,
    "mercadoriaId": 123,
    "mercadoriaCodigo": "LTBC001",
    "mercadoriaNome": "BARRA CHATA LATAO (C-270) 3/8\" X 1/16\"",
    "quantidade": 1.000,
    "unidadeMedidaSigla": "PC",
    "valorUnitario": 50.00,
    "valorTotal": 50.00
  }
]

3. Adicionar Item de Venda Casada

URL: POST /comercial/pedido/adicionar-item-venda-casada

Parâmetros (form data): - pedidoAtualId (int): ID do pedido de beneficiamento atual - pedidoVendaId (int): ID do pedido de venda a vincular - pedidoItemVendaId (int): ID do item do pedido de venda

Resposta (sucesso):

{
  "success": true,
  "message": "Item adicionado com sucesso!",
  "item": {
    "id": 13,
    "mercadoriaCodigo": "LTBC001",
    "mercadoriaNome": "BARRA CHATA LATAO",
    "quantidade": 1.000,
    "unidadeMedida": "PC",
    "valorUnitario": 50.00,
    "valorTotal": 50.00,
    "pedidoVendaVinculadoId": 15,
    "pedidoItemVendaVinculadoId": 4
  }
}

Resposta (erro):

{
  "success": false,
  "message": "Este item já foi vinculado a este pedido de Beneficiamento."
}

Implementação JavaScript

Arquivo: _ValidationScriptsPedidoPartial.cshtml

Funções principais:

// Buscar pedidos de venda para Venda Casada
function buscarPedidosVendaCasada() {
    const numeroPedido = $('#numeroPedidoVendaCasada').val();

    $.ajax({
        url: '/comercial/pedido/buscar-pedidos-venda-venda-casada',
        type: 'GET',
        data: { numeroPedido: numeroPedido },
        success: function(pedidos) {
            // Preenche dropdown de pedidos
            const select = $('#pedidoVendaCasadaSelecionado');
            select.empty().append('<option value="">Selecione...</option>');
            pedidos.forEach(p => {
                select.append(`<option value="${p.id}">
                    Pedido #${p.numero} - ${p.destinatarioNome}
                </option>`);
            });
            select.prop('disabled', false);
        }
    });
}

// Buscar itens do pedido selecionado
function buscarItensPedidoVendaCasada(pedidoVendaId) {
    $.ajax({
        url: '/comercial/pedido/buscar-itens-pedido-venda-casada',
        type: 'GET',
        data: { pedidoVendaId: pedidoVendaId },
        success: function(itens) {
            // Preenche dropdown de itens
            const select = $('#itemPedidoVendaCasadaSelecionado');
            select.empty().append('<option value="">Selecione...</option>');
            itens.forEach(i => {
                select.append(`<option value="${i.id}"
                    data-mercadoria="${i.mercadoriaNome}"
                    data-quantidade="${i.quantidade}"
                    data-unidade="${i.unidadeMedidaSigla}"
                    data-valor-unitario="${i.valorUnitario}"
                    data-valor-total="${i.valorTotal}">
                    ${i.mercadoriaCodigo} - ${i.mercadoriaNome}
                </option>`);
            });
            select.prop('disabled', false);
        }
    });
}

// Adicionar item de Venda Casada
function adicionarItemVendaCasada() {
    const pedidoAtualId = $('#Id').val();
    const pedidoVendaId = $('#pedidoVendaCasadaSelecionado').val();
    const pedidoItemVendaId = $('#itemPedidoVendaCasadaSelecionado').val();

    $.ajax({
        url: '/comercial/pedido/adicionar-item-venda-casada',
        type: 'POST',
        data: {
            pedidoAtualId: pedidoAtualId,
            pedidoVendaId: pedidoVendaId,
            pedidoItemVendaId: pedidoItemVendaId
        },
        success: function(response) {
            if (response.success) {
                alert(response.message);
                location.reload(); // Recarrega para atualizar a tabela
            } else {
                alert('Erro: ' + response.message);
            }
        }
    });
}

Configuração de Visibilidade

A visibilidade dos elementos é controlada pela função configurarNaturezaOperacao():

function configurarNaturezaOperacao(naturezaOperacao) {
    const tipoBeneficiamentoVendaCasada = 'BeneficiamentoVendaCasada';

    if (naturezaOperacao.tipoNaturezaOperacao === tipoBeneficiamentoVendaCasada) {
        // Oculta botões de inclusão manual
        $('#botoesIncluirItem').hide();
        // Exibe seção de Venda Casada
        $('#secaoVendaCasada').show();
    } else {
        // Exibe botões de inclusão manual
        $('#botoesIncluirItem').show();
        // Oculta seção de Venda Casada
        $('#secaoVendaCasada').hide();
    }
}

Estrutura de Dados

Modelo: PedidoItem

Arquivo: src/Nelmetais.SGE.Business/Models/Comercial/PedidoItem.cs

Campos adicionados:

/// <summary>
/// NH-168: ID do Pedido de Venda vinculado (Venda Casada)
/// Usado quando a Natureza de Operação é "Remessa Beneficiamento para
/// posterior retorno com Venda Casada"
/// </summary>
public int? PedidoVendaVinculadoId { get; set; }

/// <summary>
/// NH-168: Pedido de Venda vinculado (Venda Casada)
/// </summary>
public Pedido? PedidoVendaVinculado { get; set; }

/// <summary>
/// NH-168: ID do Item do Pedido de Venda vinculado (Venda Casada)
/// </summary>
public int? PedidoItemVendaVinculadoId { get; set; }

/// <summary>
/// NH-168: Item do Pedido de Venda vinculado (Venda Casada)
/// </summary>
public PedidoItem? PedidoItemVendaVinculado { get; set; }

Configuração Entity Framework

Arquivo: src/Nelmetais.SGE.Data/Configurations/Comercial/PedidoItemConfiguration.cs

// NH-168: Configuração dos campos de Venda Casada
builder.HasOne(pi => pi.PedidoVendaVinculado)
    .WithMany()
    .HasForeignKey(pi => pi.PedidoVendaVinculadoId)
    .OnDelete(DeleteBehavior.Restrict);

builder.HasOne(pi => pi.PedidoItemVendaVinculado)
    .WithMany()
    .HasForeignKey(pi => pi.PedidoItemVendaVinculadoId)
    .OnDelete(DeleteBehavior.Restrict);

Migration

Arquivo: 20251201153423_AddVendaCasadaLinksToPedidoItem.cs

migrationBuilder.AddColumn<int>(
    name: "PedidoItemVendaVinculadoId",
    table: "PedidoItem",
    type: "integer",
    nullable: true);

migrationBuilder.AddColumn<int>(
    name: "PedidoVendaVinculadoId",
    table: "PedidoItem",
    type: "integer",
    nullable: true);

migrationBuilder.AddForeignKey(
    name: "FK_PedidoItem_Pedido_PedidoVendaVinculadoId",
    table: "PedidoItem",
    column: "PedidoVendaVinculadoId",
    principalTable: "Pedido",
    principalColumn: "Id",
    onDelete: ReferentialAction.Restrict);

migrationBuilder.AddForeignKey(
    name: "FK_PedidoItem_PedidoItem_PedidoItemVendaVinculadoId",
    table: "PedidoItem",
    column: "PedidoItemVendaVinculadoId",
    principalTable: "PedidoItem",
    principalColumn: "Id",
    onDelete: ReferentialAction.Restrict);

Diagrama do Banco de Dados

┌─────────────────────────────────────────────────────────────────────────────┐
│                              TABELA: PedidoItem                             │
├─────────────────────────────────────────────────────────────────────────────┤
│  Id                         INTEGER (PK)                                    │
│  PedidoId                   INTEGER (FK → Pedido.Id)                        │
│  MercadoriaId               INTEGER (FK → Mercadoria.Id)                    │
│  Quantidade                 DECIMAL                                         │
│  ValorUnitario              DECIMAL                                         │
│  ValorTotal                 DECIMAL                                         │
│  ...                                                                        │
│  ─────────────────────────────────────────────────────────────────────────  │
│  PedidoVendaVinculadoId     INTEGER (FK → Pedido.Id) [NULLABLE]    ◄── NH-168
│  PedidoItemVendaVinculadoId INTEGER (FK → PedidoItem.Id) [NULLABLE]◄── NH-168
└─────────────────────────────────────────────────────────────────────────────┘
           │                              │
           │                              │
           ▼                              ▼
    ┌─────────────────┐           ┌─────────────────┐
    │     Pedido      │           │   PedidoItem    │
    │  (Venda)        │           │ (Item da Venda) │
    │                 │           │                 │
    │  Id = 15        │           │  Id = 4         │
    │  Natureza:      │           │  PedidoId = 15  │
    │  "Venda Produto"│           │  Mercadoria:    │
    │                 │           │  LTBC001        │
    └─────────────────┘           └─────────────────┘

Validações e Regras de Negócio

Validações Implementadas

1. Pedido Atual deve ser do tipo BeneficiamentoVendaCasada

if (pedidoAtual.NaturezaOperacao?.TipoNaturezaOperacao !=
    TipoNaturezaOperacaoEnum.BeneficiamentoVendaCasada)
{
    return Json(new {
        success = false,
        message = "O pedido atual não é do tipo 'Beneficiamento para Venda Casada'."
    });
}

2. Pedido de Venda deve existir

var pedidoVenda = await _pedidoRepository.ObterPedido(pedidoVendaId);
if (pedidoVenda == null)
{
    return Json(new {
        success = false,
        message = "Pedido de Venda não encontrado."
    });
}

3. Item do Pedido de Venda deve existir

var itemVenda = pedidoVenda.PedidosItens?.FirstOrDefault(i => i.Id == pedidoItemVendaId);
if (itemVenda == null)
{
    return Json(new {
        success = false,
        message = "Item do Pedido de Venda não encontrado."
    });
}

4. Item não pode estar já vinculado ao mesmo pedido

var itemJaVinculado = pedidoAtual.PedidosItens?.Any(i =>
    i.PedidoVendaVinculadoId == pedidoVendaId &&
    i.PedidoItemVendaVinculadoId == pedidoItemVendaId);

if (itemJaVinculado == true)
{
    return Json(new {
        success = false,
        message = "Este item já foi vinculado a este pedido de Beneficiamento."
    });
}

5. Destinatário deve ser um Fornecedor

A natureza de operação "BeneficiamentoVendaCasada" está configurada com: - NaturezaOperacaoDestinatarioId = 2 (Fornecedor)

Se o destinatário não estiver cadastrado como Fornecedor, o sistema retorna:

"A natureza de operação selecionada solicita que o destinatário informado seja um fornecedor."

Filtros na Busca de Pedidos de Venda

Apenas pedidos que atendem aos critérios são retornados:

Critério Valor
Tipo do Pedido Pedido (não Orçamento)
Tipo da Natureza Venda (TipoNaturezaOperacaoEnum.Venda)
Situação A Faturar (3) ou Fechado (4)
pedidos = pedidos
    .Where(p => p.TipoPedido?.Descricao == "Pedido")
    .Where(p => p.NaturezaOperacao?.TipoNaturezaOperacao == TipoNaturezaOperacaoEnum.Venda)
    .Where(p => p.TipoSituacaoPedido == TipoSituacaoPedidoEnum.AFaturar ||
                p.TipoSituacaoPedido == TipoSituacaoPedidoEnum.Fechado)
    .ToList();

Restrições de Ações

Implementação na View

Arquivo: _ItensPartial.cshtml

<td class="align-middle text-center">
    @* NH-168: Itens de Venda Casada só podem ser excluídos, não editados *@
    @if (pedidoItem.PedidoVendaVinculadoId == null)
    {
        <button type="submit" form="form" name="command"
                value="@PedidoCommandSubmitEnum.EditarItemEstoque"
                class="btn btn-outline-secondary btn-sm"
                title="Editar"
                onclick="selecionarPedidoItemId(@pedidoItem.Id)">
            <i class="fa fa-pen"></i>
        </button>
    }

    <button type="submit" form="form" name="command"
            value="@PedidoCommandSubmitEnum.ExcluirItemEstoque"
            class="btn btn-outline-danger btn-sm"
            title="Excluir"
            onclick="selecionarPedidoItemId(@pedidoItem.Id)">
        <i class="fa fa-trash"></i>
    </button>

    @if (pedidoItem.PedidoVendaVinculadoId == null)
    {
        <a class="btn btn-outline-primary btn-sm"
           asp-action="Edit" asp-area="Expedicao"
           asp-controller="ExpedicaoSeparacao"
           asp-route-id="@pedidoItem.Id" target="_blank"
           title="Expedição">
            <i class="fa fa-file-invoice"></i>
        </a>
    }
</td>

Resultado Visual

Tipo de Item Botões Visíveis
Item Normal [✏️ Editar] [🗑 Excluir] [📄 Expedição]
Item Venda Casada [🗑 Excluir]

Como Testar

Pré-requisitos

  1. Aplicação rodando: http://localhost:8000
  2. Usuário com permissões: Permissão de criar/editar pedidos no módulo Comercial
  3. Dados de teste:
  4. Pelo menos um Pedido de Venda com status "A Faturar" ou "Fechado"
  5. Pelo menos um Fornecedor cadastrado

Teste Completo - Passo a Passo

1. Preparar Pedido de Venda (se não existir)

  1. Acesse: Comercial > Adicionar Pedido
  2. Crie um pedido com:
  3. Natureza: "Vendas de Produto"
  4. Destinatário: Qualquer cliente
  5. Adicione pelo menos um item
  6. Avance o pedido para status "A Faturar"

2. Criar Pedido de Beneficiamento

  1. Acesse: Comercial > Adicionar Pedido
  2. Selecione Empresa de Emissão
  3. Selecione Natureza: "Remessa Beneficiamento para posterior retorno com Venda Casada"

Validar: - [ ] Botões "Novo Item de Estoque" e "Novo Item de Terceiro" estão OCULTOS - [ ] Seção "Vincular Item de Pedido de Venda (Venda Casada)" está VISÍVEL

3. Configurar Destinatário

  1. Expanda seção "Destinatário"
  2. Busque e selecione um Fornecedor

Validar: - [ ] Fornecedor foi selecionado corretamente

4. Gravar Pedido

  1. Clique em "Gravar"

Validar: - [ ] Pedido foi salvo com sucesso - [ ] Número do pedido foi gerado

5. Vincular Item de Pedido de Venda

  1. Na seção "Vincular Item de Pedido de Venda":
  2. Digite o número do Pedido de Venda
  3. Clique no botão de busca (🔍)
  4. Selecione o Pedido de Venda no dropdown
  5. Selecione o Item no dropdown
  6. Verifique as informações exibidas
  7. Clique no botão "+"

Validar: - [ ] Mensagem "Item adicionado com sucesso!" foi exibida - [ ] Página foi recarregada - [ ] Item aparece na tabela de itens

6. Verificar Restrições de Ações

  1. Localize o item adicionado na tabela

Validar: - [ ] Apenas botão de Excluir (🗑) está visível - [ ] Botões de Editar e Expedição estão OCULTOS

7. Testar Exclusão

  1. Clique no botão Excluir (🗑) do item

Validar: - [ ] Item foi excluído com sucesso

Cenários de Erro

Erro: Destinatário não é Fornecedor

Como reproduzir: 1. Selecione um destinatário que seja apenas Cliente (não cadastrado como Fornecedor) 2. Tente gravar ou excluir item

Resultado esperado:

"A natureza de operação selecionada solicita que o destinatário informado seja um fornecedor."

Solução: Cadastrar o destinatário também como Fornecedor, ou selecionar outro destinatário.

Erro: Item já vinculado

Como reproduzir: 1. Adicione um item de Venda Casada 2. Tente adicionar o mesmo item novamente

Resultado esperado:

"Este item já foi vinculado a este pedido de Beneficiamento."


Troubleshooting

Problema: "A natureza de operação selecionada solicita que o destinatário informado seja um fornecedor"

Causa: O destinatário selecionado não está cadastrado como Fornecedor.

Solução:

  1. Verificar se é Fornecedor:

    SELECT f."Id", p."Nome"
    FROM "Fornecedor" f
    JOIN "Pessoa" p ON f."PessoaId" = p."Id"
    WHERE p."Id" = <PESSOA_ID>;
    

  2. Se não for, cadastrar como Fornecedor:

    INSERT INTO "Fornecedor" ("PessoaId", "CriadoEm")
    VALUES (<PESSOA_ID>, NOW());
    

Problema: Dropdown de Pedidos de Venda vazio

Causas possíveis: 1. Não existem pedidos de venda com status "A Faturar" ou "Fechado" 2. Todos os pedidos são de tipos de natureza que não são "Venda"

Solução: Criar um pedido de venda com: - Natureza: "Vendas de Produto" ou similar (TipoNaturezaOperacao = Venda) - Avançar para status "A Faturar"

Problema: Botão "+" não funciona

Causas possíveis: 1. Pedido atual não foi gravado 2. Item não selecionado

Solução: 1. Grave o pedido primeiro (clique em "Gravar") 2. Selecione um pedido e um item nos dropdowns


Impacto no Sistema

Áreas Afetadas

Área Impacto
Módulo Comercial Tela de Pedido alterada
Banco de Dados 2 novos campos em PedidoItem
Entity Framework Nova configuração de relacionamentos

Compatibilidade

  • Retrocompatibilidade garantida: Pedidos existentes continuam funcionando
  • Campos novos são nullable: Não quebra registros existentes
  • Nenhuma alteração em APIs existentes: Novos endpoints adicionados

Performance

  • Impacto mínimo: Consultas otimizadas com AsNoTracking()
  • Índices criados: Para os novos campos de FK

Arquivos Modificados

Backend

Arquivo Tipo Descrição
PedidoItem.cs Model Adição dos campos de vínculo
PedidoItemConfiguration.cs Config Configuração das FKs
IPedidoRepository.cs Interface Novo método de busca
PedidoRepository.cs Repository Implementação da busca
PedidoController.cs Controller Novos endpoints
PedidoItemViewModel.cs ViewModel Campos de vínculo

Frontend

Arquivo Tipo Descrição
_ItensPartial.cshtml View Seção de Venda Casada, restrição de botões
_PedidoPartial.cshtml View Hidden field para suporte
_ValidationScriptsPedidoPartial.cshtml Scripts Lógica JavaScript

Database

Arquivo Tipo Descrição
20251201153423_AddVendaCasadaLinksToPedidoItem.cs Migration Criação das colunas e FKs
NelmetaisDbContextModelSnapshot.cs Snapshot Atualização do modelo

Referências

Documentos Relacionados

Glossário

Termo Definição
Venda Casada Processo onde material vendido passa por beneficiamento antes da entrega
Beneficiamento Serviço de processamento do material (corte, usinagem, etc.)
Pedido de Venda Pedido original do cliente para compra do material
Pedido de Beneficiamento Pedido de envio do material ao fornecedor para beneficiamento
Natureza de Operação Tipo de operação comercial/fiscal que define comportamentos no sistema

Histórico de Mudanças

Data Versão Alteração Autor
2025-12-01 1.0 Implementação inicial da funcionalidade Claude Code + Bruno Martins
2025-12-01 1.1 Adição da documentação técnica sobre tipo de item (sempre estoque), texto explicativo na interface e análise do SGE 2.0 Claude Code + Bruno Martins

Última atualização: 2025-12-01 Versão do documento: 1.1 Status: Implementado e documentado