using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Text; using ComplianceNFs.Core.Entities; using ComplianceNFs.Core.Ports; using Unimake.Business.DFe.Xml.NFe; namespace ComplianceNFs.Infrastructure.Parsers { public class XmlParser : IXmlParser { public ParsedInvoice Parse(Stream xmlStream) { string xml = ReadXmlFromStream(xmlStream); var nfeProc = Unimake.Business.DFe.Utility.XMLUtility.Deserializar(xml); var infNFe = nfeProc.NFe.InfNFe.First(); var comprador = infNFe.Dest; var vendedor = infNFe.Emit; var produtos = infNFe.Det; var detalhes = produtos.First(); var impostos = detalhes.Imposto; decimal somaProdutos = CalcularSomaProdutos(produtos); decimal montanteOperacao = CalcularMontanteOperacao(produtos); decimal valorFinalComImpostos = CalcularValorFinalComImpostos(infNFe); decimal icmsNF = CalcularICMS(impostos.ICMS, somaProdutos, valorFinalComImpostos); decimal valorUnitario = CalcularValorUnitario(comprador, vendedor, somaProdutos, montanteOperacao, icmsNF); return new ParsedInvoice { CnpjComp = FormatCnpjOrCpf(comprador.CNPJ ?? comprador.CPF), CnpjVend = FormatCnpjOrCpf(vendedor.CNPJ), MontNF = montanteOperacao, PrecNF = valorUnitario, ValorFinalComImpostos = valorFinalComImpostos, RsComp = DecodeHtml(comprador?.XNome), RsVend = DecodeHtml(vendedor?.XNome), NumeroNF = infNFe.Ide.NNF.ToString(), IcmsNF = icmsNF, UfComp = comprador?.EnderDest?.UF.ToString(), UfVend = vendedor?.EnderEmit?.UF.ToString() }; } private static string ReadXmlFromStream(Stream xmlStream) { using var reader = new StreamReader(xmlStream, Encoding.UTF8, true, 1024, leaveOpen: true); string xml = reader.ReadToEnd(); xmlStream.Position = 0; return xml; } private static decimal CalcularSomaProdutos(System.Collections.Generic.List produtos) { return produtos.Sum(prod => (prod.Prod.UCom == "KWH" ? prod.Prod.QCom / 1000M : prod.Prod.QCom) * (prod.Prod.UCom == "KWH" ? prod.Prod.VUnCom * 1000M : prod.Prod.VUnCom) ); } private static decimal CalcularMontanteOperacao(System.Collections.Generic.List produtos) { return produtos.Sum(prod => prod.Prod.UCom == "KWH" ? prod.Prod.QCom / 1000M : prod.Prod.QCom); } private static decimal CalcularValorFinalComImpostos(InfNFe infNFe) { if (infNFe.Pag?.DetPag?.Sum(pag => pag.VPag) > 0) return (decimal)infNFe.Pag.DetPag.Sum(pag => pag.VPag); if (infNFe.Cobr?.Fat?.VLiq > 0) return (decimal)infNFe.Cobr.Fat.VLiq; return 0; } private static decimal CalcularICMS(ICMS icms, decimal somaProdutos, decimal valorFinalComImpostos) { decimal icmsValue = GetICMS(icms); if (icmsValue == 0 && valorFinalComImpostos != 0) return 1 - (somaProdutos / valorFinalComImpostos); return icmsValue; } private static decimal CalcularValorUnitario(Dest comprador, Emit vendedor, decimal somaProdutos, decimal montanteOperacao, decimal icmsNF) { if (comprador.EnderDest.UF.ToString() == "SP" && vendedor.EnderEmit.UF.ToString() == "SP") { return montanteOperacao == 0 ? 0 : somaProdutos / montanteOperacao * (1 - icmsNF); } else { return montanteOperacao == 0 ? 0 : somaProdutos / montanteOperacao; } } private static string? FormatCnpjOrCpf(string? value) { if (string.IsNullOrWhiteSpace(value)) return null; if (long.TryParse(value, out var num)) return num.ToString("00000000000000"); return value; } private static string? DecodeHtml(string? value) { return value != null ? WebUtility.HtmlDecode(value) : null; } public static decimal GetICMS(ICMS icms) { var propICMS = icms.GetType() .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.Name.StartsWith("ICMS")) .ToList(); var primeiraProp = propICMS .Select(p => new { Prop = p, Valor = p.GetValue(icms) }) .FirstOrDefault(x => x.Valor != null); if (primeiraProp == null || primeiraProp.Valor == null) return 0; var tipo = primeiraProp.Valor.GetType(); var valor = Convert.ChangeType(primeiraProp.Valor, tipo); var listICMSReal = valor.GetType() .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.Name.StartsWith("PICMS")) .Select(p => p.GetValue(valor)) .FirstOrDefault(); decimal icmsReal = 0; if (listICMSReal != null && decimal.TryParse(listICMSReal.ToString(), out icmsReal)) icmsReal /= 100m; return icmsReal; } } }