- Added ComplianceNFs.Infrastructure.Tests project to the solution. - Implemented unit tests for AccessDbRepository, ArchivingService, AttachmentRepository, InvoiceIngestionService, MailListener, MonitorViewModel, and Worker. - Enhanced existing tests with additional assertions and mock setups. - Updated TODOs and roadmap documentation to reflect changes in service implementations and testing coverage. - Modified ComplianceNFs.sln to include new test project and adjust solution properties.
134 lines
5.4 KiB
C#
134 lines
5.4 KiB
C#
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<NfeProc>(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<Det> 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<Det> 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;
|
|
}
|
|
}
|
|
}
|