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:
- Usuário busca e seleciona uma Mercadoria
- Sistema dispara
selecionarMercadoria()→popularSelectLiga() - Ligas são filtradas de
mercadoria.mercadoriasCustosFornecedores - Select de Liga é populado apenas com ligas vinculadas
- 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 serNULL) - 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.cssrc/Nelmetais.SGE.Business/Models/Cadastros/Mercadoria.cssrc/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.cssrc/Nelmetais.SGE.Data/Repositories/Cadastros/MercadoriaRepository.cs
Controllers
src/Nelmetais.SGE.WebApp/Areas/Cadastros/Controllers/ManutencaoCustoMercadoriaController.cssrc/Nelmetais.SGE.WebApp/Areas/Comercial/Controllers/PedidoItemController.cs
Views
src/Nelmetais.SGE.WebApp/Areas/Cadastros/Views/ManutencaoCustoMercadoria/CustosMercadoriaFornecedor.cshtmlsrc/Nelmetais.SGE.WebApp/Areas/Comercial/Views/PedidoItem/ItemEstoque/Shared/_CondicaoComercialPartial.cshtmlsrc/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.cssrc/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
-
Performance: A carga de ligas é feita via eager loading no repositório de mercadorias para evitar N+1 queries
-
Validação: Não há validação obrigatória para Liga em
MercadoriaCustoFornecedorValidation, pois o campo é opcional -
Histórico: Alterações em
MercadoriaCustoFornecedorgeram registros emMercadoriaCustoFornecedorHistoricopara auditoria -
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