Pular para conteúdo

Preenchimento Automático de Tributos - Item de Terceiro (Ordem de Compra)

📋 Visão Geral

Este documento explica como funciona o preenchimento automático da seção "Condição Fiscal e Tributária da Compra" na tela de adição de itens em Ordens de Compra.

URL da tela: http://localhost:8000/comercial/adicionar-item-estoque/{pedidoId}?tipoPedidoItem=OrdemCompra

🔄 Fluxo Completo de Preenchimento

┌─────────────────────────────────────────────────────────────┐
│ 1. Usuário seleciona mercadoria                            │
└───────────────────┬─────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. JavaScript busca CFOPs disponíveis (AJAX)               │
│    Endpoint: /comercial/pedidoitem/obter-cfop-configuracoes│
└───────────────────┬─────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. CFOP é selecionado (automático ou manual)               │
└───────────────────┬─────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 4. JavaScript busca configurações ICMS/IPI/PIS-COFINS      │
│    Endpoints:                                               │
│    - /comercial/pedidoitem/obter-icms-configuracoes        │
│    - /comercial/pedidoitem/obter-ipi-configuracoes         │
│    - /comercial/pedidoitem/obter-piscofins-configuracoes   │
└───────────────────┬─────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 5. JavaScript chama endpoint calcular-tributos             │
│    Endpoint: /comercial/pedidoitem/calcular-tributos       │
└───────────────────┬─────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 6. Backend calcula impostos usando _pedidoItemService      │
└───────────────────┬─────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 7. JavaScript preenche tabela com valores calculados       │
│    Função: popularTabelaTributos(data)                     │
└─────────────────────────────────────────────────────────────┘

🎯 1. Carregamento Inicial da Página

Controller - PedidoItemController.cs

Arquivo: src/Nelmetais.SGE.WebApp/Areas/Comercial/Controllers/PedidoItemController.cs

// Linha 106-125
[Route("adicionar-item-estoque/{pedidoId:int}")]
[AuthorizeCustom(PermissaoTypeEnum.Pedido, PermissaoValueEnum.Adicionar)]
public async Task<IActionResult> Create(int pedidoId, TipoPedidoItemEnum tipoPedidoItem)
{
    var pedidoItemViewModel = new PedidoItemViewModel
    {
        PedidoId = pedidoId,
        Pedido = await ObterPedido(pedidoId),
        TipoPedidoItem = (int)tipoPedidoItem
    };

    if (tipoPedidoItem == TipoPedidoItemEnum.OrdemCompra)
        pedidoItemViewModel.OrdemCompraItem = new() {
            OrdemCompra = new() {
                NaturezaOperacaoId = (int)TipoNaturezaOperacaoEnum.Compra
            }
        };

    // Popula as listas dropdown incluindo tributos
    pedidoItemViewModel = await PopularListasDropDown(pedidoItemViewModel);
    return View(pedidoItemViewModel);
}

Método PopularListasDropDown

// Linha 267-304
private async Task<PedidoItemViewModel> PopularListasDropDown(PedidoItemViewModel viewModel)
{
    ViewBag.TiposOrigemMercadoria = await PopularTiposOrigemMercadoria();
    ViewBag.Fornecedores = await PopularFornecedores();
    ViewBag.Empresas = await PopularEmpresas();
    ViewBag.TiposNormaCorte = await PopularTiposNormaCorte();
    ViewBag.Cfops = await PopularCfops();

    if (viewModel.TipoPedidoItem == (int)TipoPedidoItemEnum.OrdemCompra)
    {
        ViewBag.EmpresasOrdemCompra = await PopularEmpresas(retornarPessoaId: false);
        ViewBag.FornecedoresOrdemCompra = await PopularFornecedorOrdemCompra(viewModel.OrdemCompraItem?.OrdemCompra?.FornecedorId);
        ViewBag.TransportadoresOrdemCompra = await PopularTransportador(viewModel.OrdemCompraItem?.OrdemCompra?.TransportadorId);

        ViewBag.FormasPagamento = await PopularFormasPagamento();
        ViewBag.TiposOrigemMercadoria = await PopularTiposOrigemMercadoria();
        ViewBag.UnidadesMedidas = await PopularUnidadesMedidas();
        ViewBag.CstIcms = await PopularCstIcms();
        ViewBag.CstIpi = await PopularCstIpi();

        if (viewModel.OrdemCompraItem?.OrdemCompra!.NaturezaOperacao is null)
            viewModel.OrdemCompraItem!.OrdemCompra!.NaturezaOperacao =
                await _naturezaOperacaoRepository.ObterNaturezaOperacao(viewModel.OrdemCompraItem!.OrdemCompra!.NaturezaOperacaoId);

        if (viewModel.OrdemCompraItem?.OrdemCompraItemImposto is not null)
        {
            if (viewModel.OrdemCompraItem.OrdemCompraItemImposto.NcmId.HasValue)
                ViewBag.Ncm = await PopularNcm(viewModel.OrdemCompraItem.OrdemCompraItemImposto.NcmId.Value);

            if (viewModel.OrdemCompraItem.OrdemCompraItemImposto.CfopId.HasValue)
                ViewBag.Cfop = await PopularCfop(viewModel.OrdemCompraItem.OrdemCompraItemImposto.CfopId.Value);
        }
    }

    // ← CHAMA PopularTributos
    viewModel = await PopularTributos(viewModel);

    return viewModel;
}

Método PopularTributos

// Linha 620-695
private async Task<PedidoItemViewModel> PopularTributos(PedidoItemViewModel viewModel)
{
    if (viewModel.MercadoriaId != null)
    {
        // Busca configurações de CFOP
        var selectCfopConfig = await PopularCfopConfiguracoes(viewModel.PedidoId, viewModel.MercadoriaId, viewModel.TipoOrigemMercadoriaId);
        if (selectCfopConfig != null)
        {
            ViewBag.CfopConfig = selectCfopConfig;

            if (viewModel.PedidoItemImposto is null)
                viewModel.PedidoItemImposto = new();

            var cfopConfigList = selectCfopConfig.Where(a => a.Value != "");
            viewModel.PedidoItemImposto!.CfopConfigIds = cfopConfigList.Select(x => int.Parse(x.Value)).ToList();

            if (selectCfopConfig != null && selectCfopConfig.Any())
            {
                int? cfopConfigId = null;
                if (viewModel.PedidoItemImposto?.CfopConfigId != null)
                    cfopConfigId = viewModel.PedidoItemImposto?.CfopConfigId;
                else if (selectCfopConfig.Count == 1)
                {
                    bool sucesso = int.TryParse(selectCfopConfig.FirstOrDefault()!.Value, out int id);
                    if (sucesso) cfopConfigId = id;
                }

                if (cfopConfigId != null)
                {
                    // Busca configurações de ICMS
                    var selectIcmsConfig = await PopularIcmsConfiguracoes(viewModel.PedidoId,
                                                                          viewModel.TipoOrigemMercadoriaId,
                                                                          viewModel.MercadoriaId,
                                                                          cfopConfigId);
                    if (selectIcmsConfig != null)
                    {
                        ViewBag.IcmsConfig = selectIcmsConfig;

                        if (viewModel.PedidoItemImposto != null)
                        {
                            var icmsConfigList = selectIcmsConfig.Where(a => a.Value != "");
                            viewModel.PedidoItemImposto!.IcmsConfigIds = icmsConfigList.Select(x => int.Parse(x.Value)).ToList();
                        }
                    }

                    // Busca configurações de IPI
                    var selectIpiConfig = await PopularIpiConfiguracoes(viewModel.PedidoId,
                                                                        viewModel.MercadoriaId,
                                                                        cfopConfigId);
                    if (selectIpiConfig != null)
                    {
                        ViewBag.IpiConfig = selectIpiConfig;

                        if (viewModel.PedidoItemImposto != null)
                        {
                            var ipiConfigList = selectIpiConfig.Where(a => a.Value != "");
                            viewModel.PedidoItemImposto!.IpiConfigIds = ipiConfigList.Select(x => int.Parse(x.Value)).ToList();
                        }
                    }

                    // Busca configurações de PIS/COFINS
                    var selectPisCofinsConfig = await PopularPisCofinsConfiguracoes(viewModel.PedidoId,
                                                                                    viewModel.MercadoriaId,
                                                                                    cfopConfigId);
                    if (selectPisCofinsConfig != null)
                    {
                        ViewBag.PisCofinsConfig = selectPisCofinsConfig;

                        if (viewModel.PedidoItemImposto != null)
                        {
                            var piCofinsConfigList = selectPisCofinsConfig.Where(a => a.Value != "");
                            viewModel.PedidoItemImposto!.PisCofinsConfigIds = piCofinsConfigList.Select(x => int.Parse(x.Value)).ToList();
                        }
                    }
                }
            }
        }
    }
    return viewModel;
}

🎯 2. Seleção de Mercadoria (Gatilho Principal)

JavaScript - _ValidationScriptsPedidoItemPartial.cshtml

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

// Linha 44
selectMercadoriaSelecionada.addEventListener('change', selecionarMercadoria);

// Linha 170-184
function selecionarMercadoria() {
    mercadoriaSelecionada = mercadorias.find(mercadoria => mercadoria.id == selectMercadoriaSelecionada.value);

    popularSelectLiga();
    popularSelectUnidadeComercial();
    popularTabelaEstoqueMercadoria();
    popularCaracteristicaMercadoria();
    popularSelectPoliticaVenda();
    popularSelectTipoServico();
    popularSelectCfop();  // ← BUSCA CFOPs

    calcularPesoTeorico();

    mercadoriaSalvaId = selectMercadoriaSelecionada.value;
}

Função popularSelectCfop

// Linha 1068-1077
function popularSelectCfop() {
    if (mercadoriaSalvaId != selectMercadoriaSelecionada.value) {
        limparOptionsSelectNoChange(selectCfopConfiguracoes);
        cfopConfigValues.value = '';

        selecionarCfop(); // ← Busca CFOPs disponíveis via AJAX

        selectCfopConfiguracoes.dispatchEvent(new Event('change'));
    }
}

// Linha 1023-1027
function selecionarCfop() {
    if (selectMercadoriaSelecionada.value) {
        popularSelectCfopConfiguracoes(@Model.PedidoId, selectMercadoriaSelecionada.value);
    }
}

🎯 3. Busca de Configurações de CFOP via AJAX

JavaScript

// Linha 870-898
function popularSelectCfopConfiguracoes(pedidoId, mercadoriaId) {
    let selectTributo = selectCfopConfiguracoes;
    let tibutoListaIds = cfopConfigValues;
    let ids = '';

    limparOptionsSelectNoChange(selectTributo);
    tibutoListaIds.value = '';

    if (pedidoId && mercadoriaId) {
        $.ajax({
            type: 'get',
            url: '/comercial/pedidoitem/obter-cfop-configuracoes',
            data: {
                pedidoId: pedidoId,
                mercadoriaId: mercadoriaId
            },
            dataType: 'json',
            contentType: 'application/json; charset=utf-8',
            async: false,
            success(data) {
                $.each(data, function (index, tributoConfiguracao) {
                    selectTributo.append(new Option(tributoConfiguracao.text, tributoConfiguracao.value))
                    ids = (ids != '' ? ids + "," : '') + tributoConfiguracao.value;
                });
                tibutoListaIds.value = ids;
            },
        });
    }
}

Controller Endpoint

// PedidoItemController.cs:781-786
[HttpGet("[controller]/obter-cfop-configuracoes")]
[AuthorizeCustom(PermissaoTypeEnum.Pedido, PermissaoValueEnum.Listar)]
public async Task<JsonResult> ObterCfopConfiguracoes(int? pedidoId, int? mercadoriaId, int? tipoOrigemMercadoriaId)
{
    return Json(await PopularCfopConfiguracoes(pedidoId, mercadoriaId, tipoOrigemMercadoriaId));
}

// Linha 521-543
private async Task<List<SelectListItem>?> PopularCfopConfiguracoes(int? pedidoId, int? mercadoriaId, int? tipoOrigemMercadoriaId)
{
    var cfopConfiguracoes = await _pedidoItemService.ObterCfopConfiguracoes(pedidoId, mercadoriaId, tipoOrigemMercadoriaId);

    if (cfopConfiguracoes != null && cfopConfiguracoes.Count > 0)
    {
        List<SelectListItem> listaCfopConfiguracoes = new();

        listaCfopConfiguracoes = _mapper.Map<List<CfopConfig>>(cfopConfiguracoes)
                                        .Select(x => new SelectListItem()
                                        {
                                            Value = x.Id.ToString(),
                                            Text = $"{x.Id} - {x.Nome}"
                                        }).ToList();

        if (listaCfopConfiguracoes.Count > 1)
            InserirSelectItemNaoEspecificado(listaCfopConfiguracoes);

        return listaCfopConfiguracoes;
    }

    return null;
}

🎯 4. Seleção de CFOP Dispara Outros Tributos

// Linha 54
selectCfopConfiguracoes.addEventListener('change', selecionarTributos);

// Linha 1050-1066
function selecionarTributos() {
    limparOptionsSelectNoChange(selectIcmsConfiguracoes);
    limparOptionsSelectNoChange(selectIpiConfiguracoes);
    limparOptionsSelectNoChange(selectPisCofinsConfiguracoes);

    icmsConfigValues.value = '';
    ipiConfigValues.value = '';
    pisCofinsConfigValues.value = '';

    selecionarIcms();      // ← Busca configurações ICMS
    selecionarIpi();       // ← Busca configurações IPI
    selecionarPisCofins(); // ← Busca configurações PIS/COFINS

    calcularTributos();    // ← CALCULA OS VALORES DOS IMPOSTOS

    configurarSelectTributos();
}

Busca de Configurações ICMS

// Linha 1029-1036
function selecionarIcms() {
    if (selectMercadoriaSelecionada.value && selectTipoOrigemMercadoria.value && selectCfopConfiguracoes.value) {
        popularSelectIcmsConfiguracoes(@Model.PedidoId,
            selectTipoOrigemMercadoria.value,
            selectMercadoriaSelecionada.value,
            selectCfopConfiguracoes.value);
    }
}

// Linha 900-930
function popularSelectIcmsConfiguracoes(pedidoId, tipoOrigemMercadoria, mercadoriaId, cfopConfigId) {
    let selectTributo = selectIcmsConfiguracoes;
    let tibutoListaIds = icmsConfigValues;
    let ids = '';

    limparOptionsSelectNoChange(selectTributo);
    tibutoListaIds.value = '';

    if (pedidoId && tipoOrigemMercadoria && mercadoriaId && cfopConfigId) {
        $.ajax({
            type: 'get',
            url: '/comercial/pedidoitem/obter-icms-configuracoes',
            data: {
                pedidoId: pedidoId,
                tipoOrigemMercadoria: tipoOrigemMercadoria,
                mercadoriaId: mercadoriaId,
                cfopConfigId: cfopConfigId
            },
            dataType: 'json',
            contentType: 'application/json; charset=utf-8',
            async: false,
            success(data) {
                $.each(data, function (index, tributoConfiguracao) {
                    selectTributo.append(new Option(tributoConfiguracao.text, tributoConfiguracao.value))
                    ids = (ids != '' ? ids + "," : '') + tributoConfiguracao.value;
                });
                tibutoListaIds.value = ids;
            },
        });
    }
}

Controller Endpoint ICMS

// PedidoItemController.cs:788-793
[HttpGet("[controller]/obter-icms-configuracoes")]
[AuthorizeCustom(PermissaoTypeEnum.Pedido, PermissaoValueEnum.Listar)]
public async Task<JsonResult> ObterIcmsConfiguracoes(int? pedidoId, int? tipoOrigemMercadoria, int? mercadoriaId, int? cfopConfigId)
{
    return Json(await PopularIcmsConfiguracoes(pedidoId, tipoOrigemMercadoria, mercadoriaId, cfopConfigId));
}

🎯 5. Cálculo dos Tributos (Parte Principal)

JavaScript - Chamada AJAX

// Linha 994-1021
function calcularTributos() {
    if (@Model.PedidoId && selectMercadoriaSelecionada.value) {
        $.ajax({
            type: 'get',
            url: '/comercial/pedidoitem/calcular-tributos',
            data: {
                pedidoId: @Model.PedidoId,
                mercadoriaId: selectMercadoriaSelecionada.value,
                cfopConfigId: selectCfopConfiguracoes.value,
                icmsConfigId: selectIcmsConfiguracoes.value,
                ipiConfigId: selectIpiConfiguracoes.value,
                pisCofinsConfigId: selectPisCofinsConfiguracoes.value,
                unidadeComercialId: selectUnidadeComercial.value,
                unidadeSeparacaoId: selectUnidadeSeparacao.value,
                quantidadeSeparacao: (inputQuantidadeSeparacao.value ? parseFloat(inputQuantidadeSeparacao.value.replace(",", ".")) : null),
                comprimentoMM: inputComprimentoEmMM.value,
                larguraMM: inputLarguraEmMM.value,
                valorUnitario: (inputValorUnitario.value ? parseFloat(inputValorUnitario.value.replace(",", ".")) : null)
            },
            dataType: 'json',
            contentType: 'application/json; charset=utf-8',
            async: false,
            success(data) {
                popularTabelaTributos(data); // ← Preenche a tabela de impostos
            },
        });
    }
}

Controller Endpoint - Cálculo de Tributos

// PedidoItemController.cs:809-842
[HttpGet("[controller]/calcular-tributos")]
[AuthorizeCustom(PermissaoTypeEnum.Pedido, PermissaoValueEnum.Listar)]
public async Task<JsonResult> CalcularTributos(int? pedidoId,
                                               int? mercadoriaId,
                                               int? cfopConfigId,
                                               int? icmsConfigId,
                                               int? ipiConfigId,
                                               int? pisCofinsConfigId,
                                               int? unidadeComercialId,
                                               int? unidadeSeparacaoId,
                                               decimal? quantidadeSeparacao,
                                               int? comprimentoMM,
                                               int? larguraMM,
                                               decimal? valorUnitario)
{
    var tributos = await _pedidoItemService
        .CalcularTributos(new PedidoTributosDto()
        {
            PedidoId = pedidoId,
            MercadoriaId = mercadoriaId,
            CfopConfigId = cfopConfigId,
            IcmsConfigId = icmsConfigId,
            IpiConfigId = ipiConfigId,
            PisCofinsConfigId = pisCofinsConfigId,
            UnidadeComercialId = unidadeComercialId,
            UnidadeSeparacaoId = unidadeSeparacaoId,
            QuantidadeSeparacao = quantidadeSeparacao,
            ComprimentoMM = comprimentoMM,
            LarguraMM = larguraMM,
            ValorUnitario = valorUnitario
        });

    return Json(tributos);
}

🎯 6. Preenchimento da Tabela de Tributos

JavaScript - Função popularTabelaTributos

// Linha 572-785
function popularTabelaTributos(tributos) {
    let tabelaTributos = document.getElementById("table-tributos");
    let body = tabelaTributos.querySelector("tbody");

    if (tributos) {
        let outerHTML = "";
        let rows = $('#table-tributos').find('tbody > tr');
        let cst = "", aliquota = "", aliquotaReducao = "", mva = "", baseCalculo = "", valor = "";
        let cst_st = "", aliquota_st = "", aliquotaReducao_st = "", mva_st = "", baseCalculo_st = "", valor_st = "";

        // Remove linhas existentes
        for (let i = 0; i < rows.length; i++)
            rows[i].remove();

        // ICMS
        if (tributos.cstIcms) {
            cst = tributos.percentualIcms ? tributos.cstIcms.codigo : "";
            aliquota = tributos.percentualIcms ? numberFormat(tributos.percentualIcms, 3) + " %" : "";
            aliquotaReducao = tributos.percentualIcms ? numberFormat(tributos.percentualIcmsReducao, 3) + " %" : "";
            baseCalculo = tributos.percentualIcms ? "R$ " + numberFormat(tributos.valorBaseCalculoIcms, 2) : "";
            valor = tributos.percentualIcms ? "R$ " + numberFormat(tributos.valorIcms, 2) : "";

            cst_st = tributos.percentualIcmsSt ? tributos.cstIcms.codigo : "";
            aliquota_st = tributos.percentualIcmsSt ? numberFormat(tributos.percentualIcmsSt, 3) + " %" : "";
            aliquotaReducao_st = tributos.percentualIcmsSt ? numberFormat(tributos.percentualIcmsStReducao, 3) + " %" : "";
            mva_st = tributos.percentualIcmsSt ? numberFormat(tributos.percentualIcmsStMva, 3) + " %" : "";
            baseCalculo_st = tributos.percentualIcmsSt ? "R$ " + numberFormat(tributos.valorBaseCalculoIcmsSt, 2) : "";
            valor_st = tributos.percentualIcmsSt ? "R$ " + numberFormat(tributos.valorIcmsSt, 2) : "";
        }

        // Insere linha ICMS
        outerHTML += `<td class="font-weight-bold align-middle text-left">ICMS</td>`
        outerHTML += `<td id="cst_icms" class="align-middle">${cst}</td>`
        outerHTML += `<td id="percentual_icms" class="align-middle">${aliquota}</td>`
        outerHTML += `<td id="percentual_reducao_icms" class="align-middle">${aliquotaReducao}</td>`
        outerHTML += `<td id="percentual_mva_icms" class="align-middle">${mva}</td>`
        outerHTML += `<td id="valor_base_icms" class="align-middle">${baseCalculo}</td>`
        outerHTML += `<td id="valor_icms" class="align-middle">${valor}</td>`

        row = body.insertRow();
        row.innerHTML = outerHTML;

        // ... Continua para ICMS ST, IPI, PIS, COFINS, FCP, etc.
    }
}

View - Tabela de Tributos

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

<!-- Linha 38-213 -->
<table id="table-tributos" class="table table-bordered table-hover table-sm">
    <thead>
        <tr class="table-secondary text-center">
            <th class="text-left" style="width: 100px;">Imposto</th>
            <th>CST</th>
            <th>Alíquota</th>
            <th>Alíquota Redução</th>
            <th>MVA</th>
            <th>Valor Base Cálculo</th>
            <th>Valor Imposto</th>
        </tr>
    </thead>
    <tbody>
        <!-- Linhas preenchidas dinamicamente via JavaScript -->
        <tr>
            <td class="font-weight-bold align-middle text-left">ICMS</td>
            <td id="cst_icms" class="align-middle">...</td>
            <td id="percentual_icms" class="align-middle">...</td>
            <td id="percentual_reducao_icms" class="align-middle">...</td>
            <td id="percentual_mva_icms" class="align-middle"></td>
            <td id="valor_base_icms" class="align-middle">...</td>
            <td id="valor_icms" class="align-middle">...</td>
        </tr>
        <!-- ICMS ST, IPI, PIS, COFINS, FCP -->
    </tbody>
</table>

⚡ Gatilhos para Recálculo Automático

Os tributos são recalculados automaticamente quando você altera:

// Event Listeners
selectCfopConfiguracoes.addEventListener('change', selecionarTributos);       // Linha 54
selectIcmsConfiguracoes.addEventListener('change', calcularTributos);         // Linha 55
selectIpiConfiguracoes.addEventListener('change', calcularTributos);          // Linha 56
selectPisCofinsConfiguracoes.addEventListener('change', calcularTributos);    // Linha 57
selectUnidadeSeparacao.addEventListener('change', calcularTributos);          // Linha 47
inputQuantidadeSeparacao.addEventListener('blur', calcularTributosQuantidadeSeparacao);  // Linha 62
inputValorUnitario.addEventListener('blur', calcularTributosValorUnitario);  // Linha 63

// NH-118: Verificar se campos existem antes de adicionar event listeners (campos ocultos para OrdemCompra)
if (inputComprimentoEmMM) {
    inputComprimentoEmMM.addEventListener('blur', calcularTributosComprimentoEmMM);  // Linha 70
}
if (inputLarguraEmMM) {
    inputLarguraEmMM.addEventListener('blur', calcularTributosLarguraEmMM);  // Linha 72
}

Campos que Disparam Recálculo

Campo Evento Descrição
CFOP Config change Muda todas as configurações tributárias
ICMS Config change Recalcula todos os impostos
IPI Config change Recalcula todos os impostos
PIS/COFINS Config change Recalcula todos os impostos
Unidade de Separação change Afeta base de cálculo
Quantidade Separação blur Afeta valor total e base de cálculo
Valor Unitário blur Afeta valor total e base de cálculo
Comprimento (MM) blur Afeta quantidade e base de cálculo
Largura (MM) blur Afeta quantidade e base de cálculo
Tipo Origem Mercadoria change Afeta configurações de ICMS

📊 Dados Retornados pelo Cálculo

O endpoint /comercial/pedidoitem/calcular-tributos retorna um objeto PedidoTributosDto com:

{
  "cstIcms": { "codigo": "00", "nome": "Tributada integralmente" },
  "percentualIcms": 18.00,
  "percentualIcmsReducao": 0.00,
  "valorBaseCalculoIcms": 1000.00,
  "valorIcms": 180.00,

  "percentualIcmsSt": 18.00,
  "percentualIcmsStReducao": 0.00,
  "percentualIcmsStMva": 30.00,
  "valorBaseCalculoIcmsSt": 1300.00,
  "valorIcmsSt": 234.00,

  "cstIpi": { "codigo": "50", "nome": "Saída tributada" },
  "percentualIpi": 5.00,
  "valorBaseCalculoIpi": 1000.00,
  "valorIpi": 50.00,

  "cstPis": { "codigo": "01", "nome": "Operação Tributável" },
  "percentualPis": 1.65,
  "valorBaseCalculoPis": 1000.00,
  "valorPis": 16.50,

  "percentualPisSt": 0.00,
  "valorBaseCalculoPisSt": 0.00,
  "valorPisSt": 0.00,

  "cstCofins": { "codigo": "01", "nome": "Operação Tributável" },
  "percentualCofins": 7.60,
  "valorBaseCalculoCofins": 1000.00,
  "valorCofins": 76.00,

  "percentualCOFINSST": 0.00,
  "valorBaseCalculoCofinsSt": 0.00,
  "valorCofinsSt": 0.00,

  "percentualFcp": 2.00,
  "valorBaseCalculoFcp": 1000.00,
  "valorFcp": 20.00,

  "percentualFcpSt": 0.00,
  "valorBaseCalculoFcpSt": 0.00,
  "valorFcpSt": 0.00
}

🔍 Arquivos Envolvidos

Backend (C#)

Arquivo Responsabilidade
PedidoItemController.cs Controla requisições HTTP e coordena serviços
IPedidoItemService Interface do serviço de cálculo de tributos
PedidoItemService Implementa lógica de cálculo de impostos
ICfopConfigRepository Busca configurações de CFOP
IIcmsConfigRepository Busca configurações de ICMS
IIpiConfigRepository Busca configurações de IPI
IPisCofinsConfigRepository Busca configurações de PIS/COFINS

Frontend (JavaScript/Razor)

Arquivo Responsabilidade
_ValidationScriptsPedidoItemPartial.cshtml JavaScript principal com lógica de interação
_FiscalTributarioPartial.cshtml View da seção de tributos
Create.cshtml / Edit.cshtml Views principais do formulário

📝 Observações Importantes

  1. Ordem de Execução: As configurações são buscadas em cascata (CFOP → ICMS → IPI → PIS/COFINS)
  2. Dependências: Cada configuração tributária depende da anterior
  3. Cálculo Automático: O cálculo é disparado automaticamente em qualquer alteração relevante
  4. Performance: Chamadas AJAX são síncronas (async: false) para garantir ordem de execução
  5. Validação: Todos os cálculos dependem de ter mercadoria selecionada e pedido válido

🚀 Fluxo Simplificado

Mercadoria → CFOP → ICMS/IPI/PIS-COFINS → Cálculo → Tabela

Cada etapa busca as configurações disponíveis no backend e, ao final, o cálculo real dos valores é feito pelo serviço _pedidoItemService.CalcularTributos().


Última atualização: 2025-11-10 Relacionado: Ver também NATUREZA-OPERACAO-ORDEM-COMPRA.md