Pular para conteúdo

Vínculo Mercadoria × Liga

Data: 17/11/2025 Categoria: Regra de Negócio

Descrição

Este documento explica como funciona o relacionamento entre Mercadoria e Liga no sistema SGE 3.0, incluindo onde é cadastrado e como é utilizado nos diferentes fluxos do sistema.

Contexto

No sistema Nelmetais SGE, as mercadorias podem ser fornecidas por diferentes fornecedores em diferentes ligas metálicas. O vínculo entre Mercadoria e Liga é fundamental para:

  • Item de Estoque: Filtrar e exibir apenas as ligas disponíveis para uma mercadoria específica
  • Item de Terceiro: Carregar todas as ligas quando o certificado é "Obrigatório, Transcrito"
  • Custo de Mercadoria: Associar custos específicos por fornecedor e liga

Estrutura de Dados

Tabela: MercadoriaCustoFornecedor

O vínculo Mercadoria × Liga é feito através da tabela intermediária MercadoriaCustoFornecedor.

Campos principais:

Campo Tipo Obrigatório Descrição
MercadoriaId int Sim FK para Mercadoria
FornecedorId int Sim FK para Fornecedor
LigaId int Não FK para Liga
QuantidadeMinimaCompra decimal Não Quantidade mínima para compra
Dimensao string Não Dimensão do produto
ComprimentoMinimo int Não Comprimento mínimo (mm)
ComprimentoMaximo int Não Comprimento máximo (mm)
ValorCustoTotal decimal Não Custo total calculado

Modelo de Domínio:

// src/Nelmetais.SGE.Business/Models/Comercial/MercadoriaCustoFornecedor.cs
public class MercadoriaCustoFornecedor : BaseEntity
{
    public int MercadoriaId { get; set; }
    public Mercadoria? Mercadoria { get; set; }

    public int FornecedorId { get; set; }
    public Fornecedor? Fornecedor { get; set; }

    public int? LigaId { get; set; }      // ← VÍNCULO COM LIGA
    public Liga? Liga { get; set; }

    // ... outros campos de custo e dimensões
}

Relacionamento no Entity Framework:

// src/Nelmetais.SGE.Data/Configurations/Comercial/MercadoriaCustoFornecedorConfiguration.cs
builder.HasOne(mcf => mcf.Liga)
    .WithMany()
    .HasForeignKey(mcf => mcf.LigaId)
    .IsRequired(false);  // Liga é opcional

Onde é Cadastrado

Interface de Cadastro

Menu: Cadastros > Manutenção de Custo de Mercadoria

Tela: ManutencaoCustoMercadoria/CustosMercadoriaFornecedor

Localização: - Controller: src/Nelmetais.SGE.WebApp/Areas/Cadastros/Controllers/ManutencaoCustoMercadoriaController.cs - View: src/Nelmetais.SGE.WebApp/Areas/Cadastros/Views/ManutencaoCustoMercadoria/CustosMercadoriaFornecedor.cshtml

A tela exibe uma lista de custos por fornecedor para cada mercadoria, incluindo: - Código e Descrição da Mercadoria - Fornecedor - Valores de Custo (Mão de Obra, Matéria Prima, Refino, Frete, etc.) - Liga (coluna específica mostrando a liga vinculada) - Dimensões e Comprimentos - Quantidade Mínima para Compra

Importação via SQL

A migração inicial importa dados da tabela legacy TBL_CUSTO_USINAS:

-- sql/027-mercadoria-custo-fornecedor.sql
INSERT INTO MercadoriaCustoFornecedor
(MercadoriaId, FornecedorId, LigaId, ...)
SELECT
    M.Id AS MercadoriaId,
    F.Id AS FornecedorId,
    L.Id as LigaId,  -- ← Vínculo estabelecido
    ...
FROM TBL_CUSTO_USINAS AS T
LEFT JOIN @LIGAS AS L ON F.Codigo = T.CD_LIGA

Como é Utilizado

1. Item de Estoque - Seleção de Liga

Quando o usuário seleciona uma Mercadoria no formulário de Item de Estoque:

Arquivo: src/Nelmetais.SGE.WebApp/Areas/Comercial/Views/PedidoItem/ItemEstoque/Shared/_ValidationScriptsPedidoItemPartial.cshtml

function popularSelectLiga() {
    if (tipoPedidoItem === TIPO_PEDIDO_ITEM_ORDEM_COMPRA) {
        // Item de Terceiro: carregar todas as ligas
        popularTodasLigas();
    } else {
        // Item de Estoque: filtrar por mercadoria
        const ligas = mercadoriaSelecionada
            ?.mercadoriasCustosFornecedores  // ← AQUI está o vínculo!
            ?.filter(mcf => mcf.liga)
            ?.map(mcf => mcf.liga) ?? [];

        limparOptionsSelect(selectLiga, true);

        ligas.sort((a, b) => a.nome.localeCompare(b.nome));

        ligas.forEach(liga => {
            const option = document.createElement('option');
            option.value = liga.id;
            option.text = liga.nome;
            selectLiga.add(option);
        });

        desabilitarElemento(selectLiga, !ligas.length);  // Habilita se houver ligas
    }
}

Fluxo:

  1. Usuário busca e seleciona uma Mercadoria
  2. Sistema dispara selecionarMercadoria()popularSelectLiga()
  3. Ligas são filtradas de mercadoria.mercadoriasCustosFornecedores
  4. Select de Liga é populado apenas com ligas vinculadas
  5. Select de Liga é habilitado

2. Item de Terceiro - Certificado Transcrito

Para Item de Terceiro com certificado "Obrigatório, Transcrito":

// Inicialização automática
if (tipoPedidoItem === TIPO_PEDIDO_ITEM_ORDEM_COMPRA &&
    tipoCertificadoId === TIPO_CERTIFICADO_TRANSCRITO) {
    popularTodasLigas();  // Carrega TODAS as ligas do sistema
}

3. Cascata Liga → Formato → Norma → Têmpera

Após selecionar a Liga, uma cascata de seleções é habilitada:

// Listener que habilita Formato quando Liga é selecionada
selectLiga.addEventListener('change', carregarFormatosPorLiga);

// Listener que habilita Norma quando Formato é selecionado
selectFormato.addEventListener('change', carregarNormasPorLigaFormato);

// Listener que habilita Têmpera quando Norma é selecionada
selectNorma.addEventListener('change', carregarTemerasPorLigaFormatoNorma);

Sequência: 1. Liga selecionada → Habilita Formato 2. Formato selecionado → Habilita Norma 3. Norma selecionada → Habilita Têmpera

Regras de Negócio

Multiplicidade

  • Uma Mercadoria pode ter várias Ligas (através de diferentes fornecedores)
  • Um Fornecedor pode fornecer uma Mercadoria em várias Ligas
  • A combinação MercadoriaId + FornecedorId + LigaId é única

Opcionalidade

  • O campo LigaId é opcional (pode ser NULL)
  • Nem toda mercadoria precisa ter liga associada
  • Fornecedor pode fornecer mercadoria sem especificar liga

Habilitação do Campo Liga

Para Item de Estoque: - Liga é habilitada após selecionar Mercadoria - Exibe apenas ligas vinculadas à mercadoria

Para Item de Terceiro: - Liga é habilitada automaticamente se certificado = "Obrigatório, Transcrito" - Exibe todas as ligas cadastradas no sistema

Arquivos Envolvidos

Modelo de Domínio

  • src/Nelmetais.SGE.Business/Models/Comercial/MercadoriaCustoFornecedor.cs
  • src/Nelmetais.SGE.Business/Models/Cadastros/Mercadoria.cs
  • src/Nelmetais.SGE.Business/Models/Cadastros/Liga.cs

Configuração EF Core

  • src/Nelmetais.SGE.Data/Configurations/Comercial/MercadoriaCustoFornecedorConfiguration.cs

Repositórios

  • src/Nelmetais.SGE.Data/Repositories/Comercial/MercadoriaCustoFornecedorRepository.cs
  • src/Nelmetais.SGE.Data/Repositories/Cadastros/MercadoriaRepository.cs

Controllers

  • src/Nelmetais.SGE.WebApp/Areas/Cadastros/Controllers/ManutencaoCustoMercadoriaController.cs
  • src/Nelmetais.SGE.WebApp/Areas/Comercial/Controllers/PedidoItemController.cs

Views

  • src/Nelmetais.SGE.WebApp/Areas/Cadastros/Views/ManutencaoCustoMercadoria/CustosMercadoriaFornecedor.cshtml
  • src/Nelmetais.SGE.WebApp/Areas/Comercial/Views/PedidoItem/ItemEstoque/Shared/_CondicaoComercialPartial.cshtml
  • src/Nelmetais.SGE.WebApp/Areas/Comercial/Views/PedidoItem/ItemEstoque/Shared/_ValidationScriptsPedidoItemPartial.cshtml

Scripts SQL

  • sql/027-mercadoria-custo-fornecedor.sql

ViewModels

  • src/Nelmetais.SGE.WebApp/Areas/Comercial/ViewModels/MercadoriaCustoFornecedorViewModel.cs
  • src/Nelmetais.SGE.WebApp/Areas/Comerciais/ViewModels/PedidoItemViewModel.cs

Exemplos Práticos

Exemplo 1: Mercadoria com Múltiplas Ligas

Mercadoria: "Barra Redonda Alumínio 1/2" (Código: 123)

Custos Fornecedores:
┌──────────────┬────────────┬────────────────┐
│ Fornecedor   │ Liga       │ Custo Total    │
├──────────────┼────────────┼────────────────┤
│ Fornecedor A │ 6061       │ R$ 25,00/kg    │
│ Fornecedor A │ 6351       │ R$ 28,00/kg    │
│ Fornecedor B │ 6061       │ R$ 24,50/kg    │
│ Fornecedor C │ 7075       │ R$ 35,00/kg    │
└──────────────┴────────────┴────────────────┘

Resultado: Ao selecionar esta mercadoria no Item de Estoque,
o select de Liga mostrará: 6061, 6351, 7075

Exemplo 2: Mercadoria sem Liga

Mercadoria: "Peça Usinada Especial" (Código: 456)

Custos Fornecedores:
┌──────────────┬────────────┬────────────────┐
│ Fornecedor   │ Liga       │ Custo Total    │
├──────────────┼────────────┼────────────────┤
│ Fornecedor X │ NULL       │ R$ 150,00/un   │
└──────────────┴────────────┴────────────────┘

Resultado: Select de Liga permanece desabilitado

Observações

  1. Performance: A carga de ligas é feita via eager loading no repositório de mercadorias para evitar N+1 queries

  2. Validação: Não há validação obrigatória para Liga em MercadoriaCustoFornecedorValidation, pois o campo é opcional

  3. Histórico: Alterações em MercadoriaCustoFornecedor geram registros em MercadoriaCustoFornecedorHistorico para auditoria

  4. Tooltip Informativo: O campo Liga no formulário de Item de Estoque possui um tooltip explicativo detalhando quando e como é habilitado (linha 96-101 de _CondicaoComercialPartial.cshtml)

Referências

  • Task relacionada à cascata Liga/Formato/Norma/Têmpera: NH-115
  • Documento sobre remoção de TipoFamilia da Liga: docs/fixes/remocao-tipo-familia-liga.md
  • Migration de criação: 20250111135936_initialcreate.cs