Import inicial: migração de arquivos da rede
This commit is contained in:
parent
9ed56aa987
commit
14cdce64af
4
.editorconfig
Normal file
4
.editorconfig
Normal file
@ -0,0 +1,4 @@
|
||||
[*.cs]
|
||||
|
||||
# CS8618: O campo não anulável precisa conter um valor não nulo ao sair do construtor. Considere adicionar o modificador "obrigatório" ou declarar como anulável.
|
||||
dotnet_diagnostic.CS8618.severity = none
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -361,3 +361,4 @@ MigrationBackup/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
.aider*
|
||||
|
||||
91
Application.cs
Normal file
91
Application.cs
Normal file
@ -0,0 +1,91 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NfProcessorApp.Infrastructure;
|
||||
using NfProcessorApp.Services;
|
||||
using NfProcessorApp.Handlers;
|
||||
using NfProcessorApp.Domain.Repositories;
|
||||
|
||||
namespace NfProcessorApp
|
||||
{
|
||||
public class Application
|
||||
{
|
||||
private readonly IFileScanner _scanner;
|
||||
private readonly IFileHandlerFactory _handlerFactory;
|
||||
private readonly IDatabaseUpdater _dbUpdater;
|
||||
private readonly ILogger<Application> _logger;
|
||||
private readonly IConfiguration _config;
|
||||
|
||||
#pragma warning disable IDE0290 // Usar construtor primário
|
||||
public Application(
|
||||
#pragma warning restore IDE0290 // Usar construtor primário
|
||||
IFileScanner scanner,
|
||||
IFileHandlerFactory handlerFactory,
|
||||
IDatabaseUpdater dbUpdater,
|
||||
ILogger<Application> logger,
|
||||
IConfiguration config)
|
||||
{
|
||||
_scanner = scanner;
|
||||
_handlerFactory = handlerFactory;
|
||||
_dbUpdater = dbUpdater;
|
||||
_logger = logger;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public static Application Build()
|
||||
{
|
||||
var config = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json", optional: false)
|
||||
.Build();
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IConfiguration>(config);
|
||||
services.AddLogging(builder => builder.AddConsole());
|
||||
services.AddTransient<IFileScanner, FileScanner>();
|
||||
services.AddTransient<IFileHandlerFactory, FileHandlerFactory>();
|
||||
services.AddTransient<ITERepository, AccessRepository>();
|
||||
services.AddTransient<IDatabaseUpdater, AccessRepository>();
|
||||
services.AddTransient<IValidadorNF, NFValidator>();
|
||||
services.AddTransient<IFileHandler, XmlFileHandler>();
|
||||
services.AddTransient<IFileHandler, PdfFileHandler>();
|
||||
services.AddTransient<Application>();
|
||||
services.AddTransient<XmlFileHandler>();
|
||||
services.AddTransient<PdfFileHandler>();
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
return provider.GetRequiredService<Application>();
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
var folder = _config["Settings:InputFolder"] ?? throw new InvalidOperationException("Settings:InputFolder não configurado.");
|
||||
|
||||
#pragma warning disable IDE0301 // Simplificar a inicialização de coleção
|
||||
var extensions = _config.GetSection("Settings:Extensions").Get<string[]>() ?? Array.Empty<string>();
|
||||
#pragma warning restore IDE0301 // Simplificar a inicialização de coleção
|
||||
var files = _scanner.ListFiles(folder, extensions);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
var handler = _handlerFactory.CreateHandler(file);
|
||||
var result = handler.Process(file);
|
||||
if (result.IsValid)
|
||||
{
|
||||
_dbUpdater.Update(result);
|
||||
_logger.LogInformation("Updated NF {NumeroNF} from file {file}", result.NumeroNF, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Invalid NF in file {file}", file);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error processing file {file}", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
Domain/Entities/NF.cs
Normal file
92
Domain/Entities/NF.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using System.Reflection;
|
||||
using NfProcessorApp.Domain.Services;
|
||||
using NfProcessorApp.Handlers;
|
||||
using Unimake.Business.DFe.Xml.NFe;
|
||||
|
||||
namespace NfProcessorApp.Domain.Entities
|
||||
{
|
||||
public class NF(InfNFe infNFe) : INFParser
|
||||
{
|
||||
private readonly InfNFe _infNFe = infNFe;
|
||||
|
||||
public string CNPJ_Comprador
|
||||
=> Convert.ToInt64(_infNFe.Dest.CNPJ ?? _infNFe.Dest.CPF)
|
||||
.ToString("00000000000000");
|
||||
|
||||
public string CNPJ_Vendedor
|
||||
=> Convert.ToInt64(_infNFe.Emit.CNPJ)
|
||||
.ToString("00000000000000");
|
||||
|
||||
public decimal Montante_operacao
|
||||
=> _infNFe.Det.Sum(d =>
|
||||
d.Prod.UCom == "KWH"
|
||||
? d.Prod.QCom / 1000m
|
||||
: d.Prod.QCom);
|
||||
|
||||
public double Soma2
|
||||
=> (double)_infNFe.Det.Sum(d =>
|
||||
((d.Prod.UCom == "KWH"
|
||||
? d.Prod.QCom / 1000m
|
||||
: d.Prod.QCom)
|
||||
* (d.Prod.UCom == "KWH"
|
||||
? d.Prod.VUnCom / 1000m
|
||||
: d.Prod.VUnCom)));
|
||||
|
||||
public double Valor_unitario => GetValorUnit();
|
||||
|
||||
public double Valor_final_com_impostos
|
||||
=> _infNFe.Pag.DetPag.Sum(p => p.VPag) != 0
|
||||
? _infNFe.Pag.DetPag.Sum(p => p.VPag)
|
||||
: _infNFe.Cobr.Fat.VLiq;
|
||||
|
||||
public string RS_Comprador
|
||||
=> _infNFe.Dest.XNome.Replace("&amp;", "&").Replace("&", "&");
|
||||
|
||||
public string RS_Vendedor
|
||||
=> _infNFe.Emit.XNome.Replace("&amp;", "&").Replace("&", "&");
|
||||
|
||||
public int Numero_NF
|
||||
=> _infNFe.Ide.NNF;
|
||||
|
||||
public double ICMS_NF => GetICMS(_infNFe.Det.First().Imposto.ICMS);
|
||||
public string UF_NF { get; set; } = infNFe.Dest.EnderDest.UF.ToString();
|
||||
public string UF_Vend { get; set; } = infNFe.Emit.EnderEmit.UF.ToString();
|
||||
public NF Parse(string caminhoArquivo)
|
||||
{
|
||||
// Implementar lógica de parsing aqui
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public double GetICMS(ICMS icms)
|
||||
{
|
||||
var propICMS = icms.GetType()
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
// Opcional: filtrar apenas pelas que começam com "ICMS"
|
||||
.Where(p => p.Name.StartsWith("ICMS"))
|
||||
.ToList();
|
||||
var primeiraProp = propICMS
|
||||
.Select(p => new { Prop = p, Valor = p.GetValue(icms) })
|
||||
.FirstOrDefault(x => x.Valor != null);
|
||||
bool test = double
|
||||
.TryParse(GetICMS(_infNFe.Det.First().Imposto.ICMS)
|
||||
.ToString(), out double ICMS);
|
||||
|
||||
if (!test && Valor_final_com_impostos != 0)
|
||||
{
|
||||
return 1 - (Soma2 / Valor_final_com_impostos);
|
||||
}
|
||||
return ICMS;
|
||||
}
|
||||
public double GetValorUnit()
|
||||
{
|
||||
if (UF_NF == "SP" && UF_Vend == "SP")
|
||||
{
|
||||
return Montante_operacao == 0 ? 0 : (Soma2 / (double)Montante_operacao) * (1 - ICMS_NF);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Montante_operacao == 0 ? 0 : (Soma2 / (double)Montante_operacao);
|
||||
}
|
||||
}
|
||||
public static NFResult Invalid(string filePath) => new() { FilePath = filePath, IsValid = false };
|
||||
}
|
||||
}
|
||||
41
Domain/Entities/TERecord.cs
Normal file
41
Domain/Entities/TERecord.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System.Numerics;
|
||||
using Unimake.Business.DFe.Xml.GNRE;
|
||||
|
||||
namespace NfProcessorApp.Domain.Entities
|
||||
{
|
||||
public class TERecord
|
||||
{
|
||||
public BigInteger Cod_TE { get; set; }
|
||||
public BigInteger Cod_Smart_unidade { get; set; }
|
||||
public int Mes { get; set; }
|
||||
public DateTime Hora_LO { get; set; }
|
||||
public string Operacao { get; set; }
|
||||
public string Tipo { get; set; }
|
||||
public DateTime Hora_NF { get; set; }
|
||||
public double Tempo_NF { get; set; }
|
||||
public string Contraparte_NF { get; set; }
|
||||
public string Energia { get; set; }
|
||||
public double Montante_NF { get; set; }
|
||||
public double Preco_NF { get; set; }
|
||||
public double Desconto_NF { get; set; }
|
||||
public double NF_c_ICMS { get; set; }
|
||||
public bool NF_recebida { get; set; }
|
||||
public bool NF_Correta { get; set; }
|
||||
public string Numero_NF { get; set; }
|
||||
public string Chave_acesso { get; set; }
|
||||
public bool Lanc_autom { get; set; }
|
||||
public double Revend_Mont { get; set; }
|
||||
public double Revend_Prec { get; set; }
|
||||
public string CNPJ_comp { get; set; }
|
||||
public string CNPJ_vend { get; set; }
|
||||
public double Mont_LO { get; set; }
|
||||
public double Prec_LO { get; set; }
|
||||
public string Contrato_CliqCCEE { get; set; }
|
||||
public string Vig_ini_CliqCCEE { get; set; }
|
||||
public string Vig_fim_CliqCCEE { get; set; }
|
||||
public string Submercado { get; set; }
|
||||
public string Consolidado { get; set; }
|
||||
public string PerfilCliqCCEE { get; set; }
|
||||
public string PerfilContr { get; set; }
|
||||
}
|
||||
}
|
||||
27
Domain/Repositories/ITERepository.cs
Normal file
27
Domain/Repositories/ITERepository.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NfProcessorApp.Domain.Entities;
|
||||
using NfProcessorApp.Handlers;
|
||||
|
||||
namespace NfProcessorApp.Domain.Repositories
|
||||
{
|
||||
public interface ITERepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Retorna todos os registros do banco cujo CNPJ comprador e vendedor coincidam,
|
||||
/// ordenados pelo campo de data (mais recentes primeiro).
|
||||
/// </summary>
|
||||
IEnumerable<TERecord> ObterCandidatos(string cnpjComprador, string cnpjVendedor);
|
||||
|
||||
/// <summary>
|
||||
/// Atualiza o registro identificando que a NF foi validada com sucesso
|
||||
/// e define a data de verificação.
|
||||
/// </summary>
|
||||
void MarcarComoCorreta(string cod_TE, string numeroNF, DateTime dataVerificacao, string caminhoDestino);
|
||||
|
||||
/// <summary>
|
||||
/// Retorna o caminho de arquivamento para este fornecedor (campo Caminho_NFs).
|
||||
/// </summary>
|
||||
string ObterCaminhoDestino(string cnpjFornecedor);
|
||||
}
|
||||
}
|
||||
12
Domain/Services/INFParser.cs
Normal file
12
Domain/Services/INFParser.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using NfProcessorApp.Domain.Entities;
|
||||
using Unimake.Business.DFe.Xml.NFe;
|
||||
|
||||
namespace NfProcessorApp.Domain.Services
|
||||
{
|
||||
public interface INFParser
|
||||
{
|
||||
NF Parse(string caminhoArquivo);
|
||||
double GetICMS(ICMS icms);
|
||||
double GetValorUnit();
|
||||
}
|
||||
}
|
||||
20
Handlers/FileHandlerFactory.cs
Normal file
20
Handlers/FileHandlerFactory.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace NfProcessorApp.Handlers
|
||||
{
|
||||
public class FileHandlerFactory(IServiceProvider provider) : IFileHandlerFactory
|
||||
{
|
||||
private readonly IServiceProvider _provider = provider;
|
||||
|
||||
public IFileHandler CreateHandler(string filePath)
|
||||
{
|
||||
var ext = Path.GetExtension(filePath).ToLowerInvariant();
|
||||
return ext switch
|
||||
{
|
||||
".xml" => _provider.GetRequiredService<XmlFileHandler>(),
|
||||
".pdf" => _provider.GetRequiredService<PdfFileHandler>(),
|
||||
_ => throw new NotSupportedException($"Extension not supported: {ext}")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
7
Handlers/IFileHandler.cs
Normal file
7
Handlers/IFileHandler.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace NfProcessorApp.Handlers
|
||||
{
|
||||
public interface IFileHandler
|
||||
{
|
||||
NFResult Process(string filePath);
|
||||
}
|
||||
}
|
||||
7
Handlers/IFileHandlerFactory.cs
Normal file
7
Handlers/IFileHandlerFactory.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace NfProcessorApp.Handlers
|
||||
{
|
||||
public interface IFileHandlerFactory
|
||||
{
|
||||
IFileHandler CreateHandler(string filePath);
|
||||
}
|
||||
}
|
||||
7
Handlers/IValidadorNF.cs
Normal file
7
Handlers/IValidadorNF.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace NfProcessorApp.Handlers
|
||||
{
|
||||
public interface IValidadorNF
|
||||
{
|
||||
bool Validate(NFResult result);
|
||||
}
|
||||
}
|
||||
34
Handlers/NFResult.cs
Normal file
34
Handlers/NFResult.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using NfProcessorApp.Domain.Entities;
|
||||
using Unimake.Business.DFe.Xml.NFe;
|
||||
|
||||
namespace NfProcessorApp.Handlers
|
||||
{
|
||||
public class NFResult
|
||||
{
|
||||
public string FilePath { get; set; }
|
||||
public string NumeroNF { get; set; }
|
||||
public string CNPJ_Comprador { get; set; }
|
||||
public string CNPJ_Vendedor { get; set; }
|
||||
public bool IsValid { get; set; }
|
||||
|
||||
public static NFResult FromPath(string filePath)
|
||||
{
|
||||
var inf = new NfeProc().LoadFromFile(filePath).NFe.InfNFe.First();
|
||||
|
||||
if (inf == null) { return Invalid(filePath); }
|
||||
|
||||
var nf = new NF(inf);
|
||||
|
||||
return new NFResult
|
||||
{
|
||||
FilePath = filePath,
|
||||
NumeroNF = nf.Numero_NF.ToString(),
|
||||
CNPJ_Comprador = nf.CNPJ_Comprador,
|
||||
CNPJ_Vendedor = nf.CNPJ_Vendedor,
|
||||
IsValid = false
|
||||
};
|
||||
}
|
||||
|
||||
public static NFResult Invalid(string filePath) => new() { FilePath = filePath, IsValid = false };
|
||||
}
|
||||
}
|
||||
11
Handlers/NFValidator.cs
Normal file
11
Handlers/NFValidator.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace NfProcessorApp.Handlers
|
||||
{
|
||||
public class NFValidator : IValidadorNF
|
||||
{
|
||||
public bool Validate(NFResult result)
|
||||
{
|
||||
// TODO: implementar lógica de validação comparando com banco Access
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Handlers/PdfFileHandler.cs
Normal file
11
Handlers/PdfFileHandler.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace NfProcessorApp.Handlers
|
||||
{
|
||||
public class PdfFileHandler : IFileHandler
|
||||
{
|
||||
public NFResult Process(string filePath)
|
||||
{
|
||||
// TODO: implementar leitura básica de PDF
|
||||
return NFResult.Invalid(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Handlers/XmlFileHandler.cs
Normal file
16
Handlers/XmlFileHandler.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Unimake.Business.DFe.Xml.NFe;
|
||||
|
||||
namespace NfProcessorApp.Handlers
|
||||
{
|
||||
public class XmlFileHandler(IValidadorNF validator) : IFileHandler
|
||||
{
|
||||
private readonly IValidadorNF _validator = validator;
|
||||
|
||||
public NFResult Process(string filePath)
|
||||
{
|
||||
var result = NFResult.FromPath(filePath);
|
||||
result.IsValid = _validator.Validate(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
133
Infrastructure/AccessRepository.cs
Normal file
133
Infrastructure/AccessRepository.cs
Normal file
@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.OleDb;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NfProcessorApp.Domain.Entities;
|
||||
using NfProcessorApp.Domain.Repositories;
|
||||
using NfProcessorApp.Handlers;
|
||||
using NfProcessorApp.Infrastructure;
|
||||
|
||||
namespace NfProcessorApp.Infrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementação de INotaFiscalRepository usando banco Access
|
||||
/// </summary>
|
||||
public class AccessRepository : ITERepository, IDatabaseUpdater
|
||||
{
|
||||
private readonly string _connectionString;
|
||||
private readonly ILogger<AccessRepository> _logger;
|
||||
|
||||
public AccessRepository(IConfiguration config, ILogger<AccessRepository> logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(config);
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
_connectionString = config.GetConnectionString("AccessDb") ?? throw new InvalidOperationException("Connection string 'AccessDb' não configurada.");
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IEnumerable<TERecord> ObterCandidatos(string cnpjComprador, string cnpjVendedor)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT Mont_LO AS Mont_LO,
|
||||
Prec_LO AS Prec_LO,
|
||||
NF_c_ICMS AS NF_c_ICMS,
|
||||
Hora_NF AS Data
|
||||
FROM Dados_TE DT
|
||||
INNER JOIN Dados_Cadastrais DC ON DT.Cod_smart_unidade = DC.Cod_smart_unidade
|
||||
WHERE DC.CNPJ_comp = @CNPJ_comp
|
||||
AND DC.CNPJ_vend = @CNPJ_vend
|
||||
ORDER BY Hora_NF DESC";
|
||||
|
||||
try
|
||||
{
|
||||
using var conn = new OleDbConnection(_connectionString);
|
||||
return conn.Query<TERecord>(sql, new
|
||||
{
|
||||
CNPJ_comp = cnpjComprador,
|
||||
CNPJ_vend = cnpjVendedor
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Erro obtendo candidatos com Dapper para {CNPJ_comp}/{CNPJ_vend}", cnpjComprador, cnpjVendedor);
|
||||
|
||||
#pragma warning disable IDE0301 // Simplificar a inicialização de coleção
|
||||
return Array.Empty<TERecord>();
|
||||
#pragma warning restore IDE0301 // Simplificar a inicialização de coleção
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void MarcarComoCorreta(string cod_TE, string numeroNF, DateTime dataVerificacao, string caminhoDestino)
|
||||
{
|
||||
const string sql = @"
|
||||
UPDATE Dados_Cadastrais
|
||||
SET Numero_NF = @Numero_NF,
|
||||
NF_Correta = TRUE,
|
||||
Hora_NF = @Hora_NF,
|
||||
Caminho_NFs = @Caminho_NFs
|
||||
WHERE Cod_TE = @Cod_TE";
|
||||
|
||||
try
|
||||
{
|
||||
using var conn = new OleDbConnection(_connectionString);
|
||||
var affected = conn.Execute(sql, new
|
||||
{
|
||||
Cod_TE = cod_TE,
|
||||
Numero_NF = numeroNF,
|
||||
Hora_NF = dataVerificacao,
|
||||
Caminho_NFs = caminhoDestino
|
||||
});
|
||||
_logger.LogInformation("Registros atualizados: {AffectedRows}", affected);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Erro ao marcar NF como correta com Dapper para Cod_TE {cod_TE}", cod_TE);
|
||||
}
|
||||
}
|
||||
|
||||
public string ObterCaminhoDestino(string cnpjComprador)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT Caminho_NFs
|
||||
FROM Dados_Cadastrais
|
||||
WHERE CNPJ_CPF = @CNPJ_comp";
|
||||
|
||||
try
|
||||
{
|
||||
using var conn = new OleDbConnection(_connectionString);
|
||||
return conn.QueryFirstOrDefault<string>(sql, new { CNPJ_comp = cnpjComprador })
|
||||
?? string.Empty;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Erro ao obter caminho de destino para o cliente com CNPJ {CNPJ}", cnpjComprador);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Update(NFResult result)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(result);
|
||||
|
||||
// Data de verificação = momento atual
|
||||
var dataVerificacao = DateTime.Now;
|
||||
|
||||
// Obtém caminho de destino usando o CNPJ do vendedor
|
||||
var caminhoDestino = ObterCaminhoDestino(result.CNPJ_Vendedor);
|
||||
|
||||
var candidatos = ObterCandidatos(result.CNPJ_Comprador, result.CNPJ_Vendedor);
|
||||
|
||||
// Chama o método de marcação usando Cod_TE e NumeroNF do resultado
|
||||
MarcarComoCorreta(
|
||||
cod_TE: candidatos.First().Cod_TE.ToString(),
|
||||
numeroNF: result.NumeroNF,
|
||||
dataVerificacao: dataVerificacao,
|
||||
caminhoDestino: caminhoDestino
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Infrastructure/IDatabaseUpdater.cs
Normal file
9
Infrastructure/IDatabaseUpdater.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using NfProcessorApp.Handlers;
|
||||
|
||||
namespace NfProcessorApp.Infrastructure
|
||||
{
|
||||
public interface IDatabaseUpdater
|
||||
{
|
||||
void Update(NFResult result);
|
||||
}
|
||||
}
|
||||
51
NfProcessorApp.csproj
Normal file
51
NfProcessorApp.csproj
Normal file
@ -0,0 +1,51 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0-windows</TargetFramework>
|
||||
<NeutralResourcesLanguage>pt-BR</NeutralResourcesLanguage>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Models\**" />
|
||||
<EmbeddedResource Remove="Models\**" />
|
||||
<None Remove="Models\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.0-preview.3.25171.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.0-preview.3.25171.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-preview.3.25171.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.0-preview.3.25171.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.0-preview.3.25171.5" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="System.Data.OleDb" Version="10.0.0-preview.3.25171.5" />
|
||||
<PackageReference Include="Unimake.DFe" Version="20250507.1733.30" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -1,29 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0-windows</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Data.OleDb" Version="9.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -1,9 +1,9 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.12.35527.113 d17.12
|
||||
VisualStudioVersion = 17.12.35527.113
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OldComplianceNFs", "OldComplianceNFs.csproj", "{6497B886-8A32-4965-8CA0-3A241B98B81A}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NfProcessorApp", "NfProcessorApp.csproj", "{6497B886-8A32-4965-8CA0-3A241B98B81A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
||||
323
Program.cs
323
Program.cs
@ -1,45 +1,40 @@
|
||||
using System.Data;
|
||||
using System.Data.OleDb;
|
||||
//namespace NfProcessorApp
|
||||
//{
|
||||
// class Program
|
||||
// {
|
||||
// static void Main()
|
||||
// {
|
||||
// // Caminho da pasta configurado em appsettings.json
|
||||
// var app = Application.Build();
|
||||
// app.Run();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
using Unimake.Business.DFe.Xml.NFe;
|
||||
using Emit = Unimake.Business.DFe.Xml.NFe.Emit;
|
||||
using System.Reflection;
|
||||
|
||||
class Program
|
||||
{
|
||||
static readonly DataTable dados = new();
|
||||
static void ObterDadosAccess(string connectionString, string query)
|
||||
{
|
||||
using OleDbConnection connection = new(connectionString);
|
||||
OleDbCommand command = new(query, connection);
|
||||
connection.Open();
|
||||
var reader = command.ExecuteReader();
|
||||
|
||||
dados.Load(reader);
|
||||
|
||||
//reader.Close();
|
||||
//command.Dispose();
|
||||
//connection.Close();
|
||||
}
|
||||
static async Task Main()
|
||||
{
|
||||
string pasta = @"X:\Back\Controle NFs\NFs";
|
||||
var arquivosXML = Directory.GetFiles(pasta, "*.xml");
|
||||
var arquivosXML = Directory.GetFiles(pasta, "*.Xml");
|
||||
var arquivosPDF = Directory.GetFiles(pasta, "*.pdf");
|
||||
var arquivos = arquivosXML.Concat(arquivosPDF);
|
||||
var tarefas = new List<Task<string>>();
|
||||
|
||||
// Obter dados do banco de dados Access
|
||||
string connectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=X:/Middle/Informativo Setorial/Modelo Word/BD1_dados cadastrais e faturas.accdb;Jet OLEDB:Database Password=gds21";
|
||||
string query = "SELECT * FROM Dados_TE LEFT JOIN Dados_Cadastrais ON Dados_TE.Cod_smart_unidade = Dados_Cadastrais.Cod_smart_unidade";
|
||||
ObterDadosAccess(connectionString, query);
|
||||
|
||||
// Processar arquivosXML em paralelo
|
||||
var linhas = new List<string>();
|
||||
//Processar arquivosXML em paralelo
|
||||
foreach (string arquivo in arquivos)
|
||||
{
|
||||
tarefas.Add(ProcessarXMLAsync(arquivo));
|
||||
//linhas.Add(await ProcessarXMLAsync(arquivo));
|
||||
}
|
||||
|
||||
var linhas = await Task.WhenAll(tarefas);
|
||||
linhas.AddRange(await Task.WhenAll(tarefas));
|
||||
|
||||
// Gerar CSV
|
||||
StringBuilder csv = new();
|
||||
@ -49,15 +44,8 @@ class Program
|
||||
csv.AppendLine(linha);
|
||||
}
|
||||
|
||||
//StringBuilder csv = new();
|
||||
|
||||
//foreach (string arquivo in arquivos)
|
||||
//{
|
||||
// csv.AppendLine(await ProcessarXMLAsync(arquivo));
|
||||
//}
|
||||
|
||||
// Escrever CSV no StdOut (VBA vai capturar)
|
||||
//Console.OutputEncoding = Encoding.UTF8;
|
||||
Console.OutputEncoding = Encoding.Latin1;
|
||||
Console.Write(csv.ToString());
|
||||
//dados.Dispose();
|
||||
}
|
||||
@ -72,131 +60,182 @@ class Program
|
||||
}
|
||||
static async Task<string> ProcessarXMLAsync(string caminhoArquivo)
|
||||
{
|
||||
string? CNPJ_Comprador = "";
|
||||
string? CNPJ_Vendedor = "";
|
||||
float? Montante_operação = null;
|
||||
float? Valor_unitário = null;
|
||||
float? Valor_final_c_impostos = null;
|
||||
string? RS_Comprador = "";
|
||||
string? RS_Vendedor = "";
|
||||
int? Numero_NF = null;
|
||||
float? ICMS_NF = null;
|
||||
string? UF_NF = "";
|
||||
string? UF_Vend = "";
|
||||
string? Cod_TE = "";
|
||||
|
||||
if (Path.GetExtension(caminhoArquivo).Equals(".xml", StringComparison.CurrentCultureIgnoreCase))
|
||||
NFresponse response = new();
|
||||
string status = "";
|
||||
try
|
||||
{
|
||||
if (caminhoArquivo == "X:\\Back\\Controle NFs\\NFs\\581556552,910625 - NFE31250317155730000164550010000669361412505682.XML") { ICMS_NF = 0; }
|
||||
XDocument xml = XDocument.Load(caminhoArquivo);
|
||||
XNamespace ns = "http://www.portalfiscal.inf.br/nfe"; // Namespace do XML
|
||||
if (Path.GetExtension(caminhoArquivo).Equals(".Xml", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
//if (caminhoArquivo == @"X:\Back\Controle NFs\NFs\345425911,145611 - NFe35250603984862000194550010001466521382788552-procNFe.xml")
|
||||
//{
|
||||
// Thread.Sleep(1000);
|
||||
//}
|
||||
NF Current_NF = new(caminhoArquivo);
|
||||
|
||||
CNPJ_Comprador = xml.Descendants(ns + "dest").Elements(ns + "CNPJ").FirstOrDefault()?.Value ?? xml.Descendants(ns + "dest").Elements(ns + "CPF").FirstOrDefault()?.Value;
|
||||
CNPJ_Comprador = Convert.ToInt64(CNPJ_Comprador).ToString(@"00000000000000");
|
||||
InfNFe infNFe = new NfeProc().LoadFromFile(caminhoArquivo).NFe.InfNFe.First();
|
||||
Dest Comprador = infNFe.Dest;
|
||||
Emit Vendedor = infNFe.Emit;
|
||||
List<Det> Produtos = infNFe.Det;
|
||||
|
||||
CNPJ_Vendedor = xml.Descendants(ns + "emit").Elements(ns + "CNPJ").FirstOrDefault()?.Value;
|
||||
CNPJ_Vendedor = Convert.ToInt64(CNPJ_Vendedor).ToString(@"00000000000000");
|
||||
float soma1 = Produtos.Sum(prod => (float?)prod.Prod.QCom ?? 0);
|
||||
decimal soma2 = 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
|
||||
)
|
||||
));
|
||||
|
||||
RS_Comprador = xml.Descendants(ns + "dest").Elements(ns + "xNome").FirstOrDefault()?.Value;
|
||||
RS_Vendedor = xml.Descendants(ns + "emit").Elements(ns + "xNome").FirstOrDefault()?.Value;
|
||||
|
||||
float soma1 = xml.Descendants(ns + "prod").Sum(prod => (float?)prod.Element(ns + "qCom") ?? 0);
|
||||
float soma2 = xml.Descendants(ns + "prod").Sum(prod => ((float?)prod.Element(ns + "qCom") ?? 0) * ((float?)prod.Element(ns + "vUnCom") ?? 0));
|
||||
|
||||
if (CNPJ_Vendedor == "06981176000158" || CNPJ_Vendedor == "06981176002100" || CNPJ_Vendedor == "17155730000164")
|
||||
if (Current_NF.CNPJ_Vendedor == "06981176000158" || Current_NF.CNPJ_Vendedor == "06981176002100" || Current_NF.CNPJ_Vendedor == "17155730000164")
|
||||
{
|
||||
soma1 /= 1000;
|
||||
}
|
||||
|
||||
Montante_operação = soma1;
|
||||
if (soma1 != 0)
|
||||
{
|
||||
Valor_unitário = soma2 / soma1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Valor_unitário = 0;
|
||||
decimal Valor_unitário = soma2 / (decimal)soma1;
|
||||
}
|
||||
|
||||
Valor_final_c_impostos = float.TryParse(xml.Descendants(ns + "pag").Elements(ns + "detPag").Elements(ns + "vPag").FirstOrDefault()?.Value.Replace(".",","), out float valor) ? valor : 0;
|
||||
|
||||
if (Valor_final_c_impostos == 0)
|
||||
{
|
||||
Valor_final_c_impostos = float.TryParse(xml.Descendants(ns + "cobr").Elements(ns + "fat").Elements(ns + "vLiq").FirstOrDefault()?.Value.Replace(".", ","), out float valor2) ? valor2 : 0;
|
||||
}
|
||||
|
||||
Numero_NF = int.Parse(xml.Descendants(ns + "ide").Elements(ns + "nNF").FirstOrDefault()?.Value ?? "0");
|
||||
|
||||
ICMS_NF = (float.TryParse(xml.Descendants(ns + "imposto").Elements(ns + "ICMS").Elements(ns + "ICMS00").Elements(ns + "pICMS").FirstOrDefault()?.Value.Replace(".", ","), out float valor1) ? valor1 : 0)/100;
|
||||
|
||||
if (ICMS_NF == 0 && Valor_final_c_impostos != 0) {
|
||||
ICMS_NF = 1 - (soma2 / Valor_final_c_impostos);
|
||||
}
|
||||
|
||||
UF_NF = xml.Descendants(ns + "dest").Elements(ns + "enderDest").Elements(ns + "UF").FirstOrDefault()?.Value;
|
||||
UF_Vend = xml.Descendants(ns + "emit").Elements(ns + "enderEmit").Elements(ns + "UF").FirstOrDefault()?.Value;
|
||||
|
||||
if (UF_NF == "SP" && UF_Vend == "SP")
|
||||
{
|
||||
Valor_unitário *= (1 - ICMS_NF);
|
||||
}
|
||||
|
||||
Cod_TE = Id_operacao(CNPJ_Comprador, CNPJ_Vendedor, Montante_operação ?? 0, Valor_unitário ?? 0, Valor_final_c_impostos ?? 0);
|
||||
}
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
// Formatar como CSV (com proteção contra vírgulas)
|
||||
return $"{ProcessarArquivo(caminhoArquivo)};\"{
|
||||
CNPJ_Comprador}\";\"{
|
||||
CNPJ_Vendedor}\";\"{
|
||||
Montante_operação.ToString()?.Replace(',','.')}\";\"{
|
||||
Valor_unitário.ToString()?.Replace(',', '.')}\";\"{
|
||||
Valor_final_c_impostos.ToString()?.Replace(',','.')}\";\"{
|
||||
RS_Comprador}\";\"{
|
||||
RS_Vendedor}\";\"{
|
||||
Numero_NF}\";\"{
|
||||
ICMS_NF.ToString()?.Replace(',','.')}\";\"{
|
||||
UF_NF}\"";
|
||||
//Cod_TE}\"";
|
||||
|
||||
});
|
||||
}
|
||||
static string Id_operacao(string CNPJ_Comprador, string CNPJ_Vendedor, float Montante_operação, float Valor_unitário, float Valor_final_c_impostos)
|
||||
{
|
||||
float tolerancia_montante = 0.01f;
|
||||
float tolerancia_valor_unitario = 0.005f;
|
||||
float tolerancia_valor_final = 0.01f;
|
||||
|
||||
try
|
||||
{
|
||||
var selecao = dados.Select($"CNPJ_comp = {CNPJ_Comprador} AND CNPJ_vend = {CNPJ_Vendedor}", $"Mes DESC");
|
||||
|
||||
foreach (var row in selecao)
|
||||
{
|
||||
double mont_LO = row.Field<double>("Mont_LO");
|
||||
double prec_LO = row.Field<double>("Prec_LO");
|
||||
double NF_c_ICMS = row.Field<double>("NF_c_ICMS");
|
||||
|
||||
bool montDentroDaTolerancia = Math.Abs(mont_LO - Montante_operação) / Montante_operação < tolerancia_montante;
|
||||
bool unitDentroDaTolerancia = Math.Abs(prec_LO - Valor_unitário) / Valor_unitário < tolerancia_valor_unitario;
|
||||
bool valorDentroDaTolerancia = Math.Abs(NF_c_ICMS - Valor_final_c_impostos) / Valor_final_c_impostos < tolerancia_valor_final;
|
||||
|
||||
if (montDentroDaTolerancia && unitDentroDaTolerancia)
|
||||
{
|
||||
return row.Field<string>("Cod_TE")!;
|
||||
}
|
||||
else if (valorDentroDaTolerancia)
|
||||
{
|
||||
return row.Field<string>("Cod_TE")!;
|
||||
}
|
||||
response.CNPJ_Comprador = Current_NF.CNPJ_Comprador;
|
||||
response.CNPJ_Vendedor = Current_NF.CNPJ_Vendedor;
|
||||
response.Montante_operação = Current_NF.Montante_operação;
|
||||
response.Valor_unitário = Current_NF.Valor_unitário;
|
||||
response.Valor_final_c_impostos = Current_NF.Valor_final_c_impostos;
|
||||
response.RS_Comprador = Current_NF.RS_Comprador ?? "";
|
||||
response.RS_Vendedor = Current_NF.RS_Vendedor ?? "";
|
||||
response.Numero_NF = Current_NF.Numero_NF ?? 0;
|
||||
response.ICMS_NF = Current_NF.ICMS_NF;
|
||||
status = Current_NF.UF_NF ?? "";
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return "NAO IDENTIFICADA";
|
||||
status = "XML vazio";
|
||||
}
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
return $"{ProcessarArquivo(caminhoArquivo)};\"{response.CNPJ_Comprador}\";\"{response.CNPJ_Vendedor}\";\"{response.Montante_operação.ToString()?.Replace(',', '.')}\";\"{response.Valor_unitário.ToString()?.Replace(',', '.')}\";\"{response.Valor_final_c_impostos.ToString()?.Replace(',', '.')}\";\"{response.RS_Comprador}\";\"{response.RS_Vendedor}\";\"{response.Numero_NF}\";\"{response.ICMS_NF.ToString()?.Replace(',', '.')}\";\"{status}\"";
|
||||
});
|
||||
}
|
||||
public static double GetICMS(ICMS icms)
|
||||
{
|
||||
var propICMS = icms.GetType()
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
// Opcional: filtrar apenas pelas que começam com "ICMS"
|
||||
.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) { return 0; }
|
||||
if (primeiraProp.Valor == null) { return 0; }
|
||||
|
||||
return "NAO IDENTIFICADA";
|
||||
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();
|
||||
|
||||
double ICMSReal = 0;
|
||||
if (ListICMSReal != null && double.TryParse(ListICMSReal.ToString(), out ICMSReal))
|
||||
{
|
||||
ICMSReal /= 100d; // Convert percentage to decimal
|
||||
}
|
||||
return ICMSReal;
|
||||
}
|
||||
public class NFresponse
|
||||
{
|
||||
public string CNPJ_Comprador { get; set; } = "";
|
||||
public string CNPJ_Vendedor { get; set; } = "";
|
||||
public decimal Montante_operação { get; set; } = 0;
|
||||
public decimal Valor_unitário { get; set; } = 0;
|
||||
public double Valor_final_c_impostos { get; set; } = 0;
|
||||
public string RS_Comprador { get; set; } = "";
|
||||
public string RS_Vendedor { get; set; } = "";
|
||||
public int Numero_NF { get; set; } = 0;
|
||||
public double ICMS_NF { get; set; } = 0;
|
||||
}
|
||||
public class NF
|
||||
(
|
||||
string caminhoArquivo
|
||||
)
|
||||
{
|
||||
readonly InfNFe infNFe = new NfeProc().LoadFromFile(caminhoArquivo).NFe.InfNFe.First();
|
||||
Dest Comprador => infNFe.Dest;
|
||||
Emit Vendedor => infNFe.Emit;
|
||||
Det Detalhes => infNFe.Det.First();
|
||||
List<Det> Produtos => infNFe.Det;
|
||||
Imposto Impostos => Detalhes.Imposto;
|
||||
public string NomeArquivo => Path.GetFileName(caminhoArquivo);
|
||||
public string? TipoArquivo => Path.GetExtension(caminhoArquivo).TrimStart('.');
|
||||
public string? Referencia => this.NomeArquivo.Split(" - ").FirstOrDefault() ?? "";
|
||||
public string CNPJ_Comprador => Convert.ToInt64(Comprador.CNPJ ?? Comprador.CPF).ToString(@"00000000000000");
|
||||
public string CNPJ_Vendedor => Convert.ToInt64(Vendedor.CNPJ).ToString(@"00000000000000");
|
||||
|
||||
public decimal Soma2 =>
|
||||
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
|
||||
)
|
||||
));
|
||||
|
||||
public decimal Montante_operação
|
||||
=>
|
||||
Produtos.Sum(prod =>
|
||||
(
|
||||
prod.Prod.UCom == "KWH" ?
|
||||
prod.Prod.QCom / 1000M :
|
||||
prod.Prod.QCom
|
||||
)
|
||||
);
|
||||
public decimal Valor_unitário =>
|
||||
(
|
||||
Comprador.EnderDest.UF.ToString() == "SP" &&
|
||||
Vendedor.EnderEmit.UF.ToString() == "SP"
|
||||
)
|
||||
?
|
||||
(
|
||||
Montante_operação == 0
|
||||
?
|
||||
0
|
||||
:
|
||||
(Soma2 / Montante_operação) * (1 - (decimal)ICMS_NF))
|
||||
:
|
||||
(
|
||||
Montante_operação == 0
|
||||
?
|
||||
0
|
||||
:
|
||||
(Soma2 / Montante_operação)
|
||||
);
|
||||
public double Valor_final_c_impostos => (infNFe.Pag.DetPag.Sum(pag => (pag.VPag)) != 0 ? infNFe.Pag.DetPag.Sum(pag => (pag.VPag)) : ((infNFe.Cobr == null ? infNFe.Total.ICMSTot.VNF : infNFe.Cobr.Fat.VLiq)));
|
||||
public string? RS_Comprador => Comprador.XNome.Replace("&amp;", "&").Replace("&", "&");
|
||||
public string? RS_Vendedor => Vendedor.XNome.Replace("&amp;", "&").Replace("&", "&");
|
||||
public int? Numero_NF => infNFe.Ide.NNF;
|
||||
public double ICMS_NF => (GetICMS(Impostos.ICMS) == 0 && Valor_final_c_impostos != 0) ? 1 - ((double)Soma2 / Valor_final_c_impostos) : GetICMS(Impostos.ICMS);
|
||||
public string? UF_NF => Comprador.EnderDest.UF.ToString();
|
||||
public string? UF_Vend => Vendedor.EnderEmit.UF.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
4
Properties/Resources.Designer.cs
generated
4
Properties/Resources.Designer.cs
generated
@ -8,7 +8,7 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace OldComplianceNFs.Properties {
|
||||
namespace NfProcessorApp.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ namespace OldComplianceNFs.Properties {
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OldComplianceNFs.Properties.Resources", typeof(Resources).Assembly);
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NfProcessorApp.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
|
||||
13
Services/FileScanner.cs
Normal file
13
Services/FileScanner.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace NfProcessorApp.Services
|
||||
{
|
||||
public class FileScanner : IFileScanner
|
||||
{
|
||||
public IEnumerable<string> ListFiles(string folderPath, string[] extensions)
|
||||
{
|
||||
return Directory
|
||||
.EnumerateFiles(folderPath)
|
||||
.Where(f => extensions.Contains(Path.GetExtension(f), System.StringComparer.OrdinalIgnoreCase))
|
||||
.OrderBy(f => f);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
Services/IFileScanner.cs
Normal file
7
Services/IFileScanner.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace NfProcessorApp.Services
|
||||
{
|
||||
public interface IFileScanner
|
||||
{
|
||||
IEnumerable<string> ListFiles(string folderPath, string[] extensions);
|
||||
}
|
||||
}
|
||||
17
Tests/Handlers/NFValidatorTests.cs
Normal file
17
Tests/Handlers/NFValidatorTests.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using NfProcessorApp.Handlers;
|
||||
using Xunit;
|
||||
|
||||
namespace NfProcessorApp.Tests.Handlers
|
||||
{
|
||||
public class NFValidatorTests
|
||||
{
|
||||
private readonly NFValidator _validator = new();
|
||||
|
||||
[Fact]
|
||||
public void Validate_Always_ReturnsTrue() // TODO: adjust when real logic implemented
|
||||
{
|
||||
var dummy = NFResult.Invalid("file");
|
||||
Assert.True(_validator.Validate(dummy));
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Tests/Handlers/PdfFileHandlerTests.cs
Normal file
16
Tests/Handlers/PdfFileHandlerTests.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using NfProcessorApp.Handlers;
|
||||
using Xunit;
|
||||
|
||||
namespace NfProcessorApp.Tests.Handlers
|
||||
{
|
||||
public class PdfFileHandlerTests
|
||||
{
|
||||
[Fact]
|
||||
public void Process_Always_ReturnsInvalid()
|
||||
{
|
||||
var handler = new PdfFileHandler();
|
||||
var result = handler.Process("dummy.pdf");
|
||||
Assert.False(result.IsValid);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Tests/Handlers/XmlFileHandlerTests.cs
Normal file
27
Tests/Handlers/XmlFileHandlerTests.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using NfProcessorApp.Handlers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace NfProcessorApp.Tests.Handlers
|
||||
{
|
||||
public class XmlFileHandlerTests
|
||||
{
|
||||
[Fact]
|
||||
public void Process_ValidXml_ReturnsValidResult()
|
||||
{
|
||||
// Arrange
|
||||
var mockValidator = new Mock<IValidadorNF>();
|
||||
mockValidator.Setup(v => v.Validate(It.IsAny<NFResult>())).Returns(true);
|
||||
var handler = new XmlFileHandler(mockValidator.Object);
|
||||
var tempFile = Path.GetTempFileName().Replace(".tmp", ".xml");
|
||||
File.WriteAllText(tempFile, "<?xml version=\"1.0\"?><nfeProc xmlns=\"http://www.portalfiscal.inf.br/nfe\"><NFe><infNFe versao=\"4.00\"><ide><cUF>35</cUF><cNF>123</cNF><natOp>Test</natOp><mod>55</mod><serie>1</serie><nNF>1</nNF></ide><emit><CNPJ>00000000000191</CNPJ><xNome>Test</xNome></emit></infNFe></NFe></nfeProc>");
|
||||
|
||||
// Act
|
||||
var result = handler.Process(tempFile);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsValid);
|
||||
Assert.Equal("1", result.NumeroNF);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Tests/Infrastructure/AccessRepositoryTests.cs
Normal file
28
Tests/Infrastructure/AccessRepositoryTests.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NfProcessorApp.Handlers;
|
||||
using NfProcessorApp.Infrastructure;
|
||||
using Xunit;
|
||||
|
||||
namespace NfProcessorApp.Tests.Infrastructure
|
||||
{
|
||||
public class AccessRepositoryTests
|
||||
{
|
||||
[Fact]
|
||||
public void Update_InvalidResult_DoesNotThrow()
|
||||
{
|
||||
// Arrange
|
||||
var inMemConfig = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection([new KeyValuePair<string, string?>("ConnectionStrings:AccessDb", "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=:memory:;")])
|
||||
.Build();
|
||||
var mockLogger = new Mock<ILogger<AccessRepository>>();
|
||||
var repo = new AccessRepository(inMemConfig, mockLogger.Object);
|
||||
var result = NFResult.Invalid("file");
|
||||
|
||||
// Act & Assert: should not throw even if DB unaccessible
|
||||
var ex = Record.Exception(() => "Não concordância");
|
||||
Assert.Null(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Tests/Services/FileScannerTests.cs
Normal file
38
Tests/Services/FileScannerTests.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using NfProcessorApp.Services;
|
||||
using Xunit;
|
||||
|
||||
namespace NfProcessorApp.Tests.Services
|
||||
{
|
||||
public class FileScannerTests
|
||||
{
|
||||
private readonly FileScanner _scanner = new();
|
||||
|
||||
[Fact]
|
||||
public void ListFiles_InvalidFolder_ThrowsDirectoryNotFoundException()
|
||||
{
|
||||
Assert.Throws<DirectoryNotFoundException>(() => _scanner.ListFiles("nonexistent", [".xml"]).ToList());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListFiles_FiltersByExtension_OnlyReturnsMatchingFiles()
|
||||
{
|
||||
// Arrange: create temporary folder with files
|
||||
var temp = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
Directory.CreateDirectory(temp);
|
||||
File.WriteAllText(Path.Combine(temp, "a.xml"), "");
|
||||
File.WriteAllText(Path.Combine(temp, "b.pdf"), "");
|
||||
File.WriteAllText(Path.Combine(temp, "c.txt"), "");
|
||||
|
||||
// Act
|
||||
var result = _scanner.ListFiles(temp, [".xml", ".pdf"]).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Contains(Path.Combine(temp, "a.xml"), result);
|
||||
Assert.Contains(Path.Combine(temp, "b.pdf"), result);
|
||||
Assert.DoesNotContain(Path.Combine(temp, "c.txt"), result);
|
||||
|
||||
// Cleanup
|
||||
Directory.Delete(temp, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
appsettings.json
Normal file
17
appsettings.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"exclude": [
|
||||
"**/bin",
|
||||
"**/bower_components",
|
||||
"**/jspm_packages",
|
||||
"**/node_modules",
|
||||
"**/obj",
|
||||
"**/platforms"
|
||||
],
|
||||
"Settings": {
|
||||
"InputFolder": "X:\\Back\\Controle NFs\\NFs",
|
||||
"Extensions": [ ".xml", ".pdf" ]
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"AccessDb": "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=X:/Middle/Informativo Setorial/Modelo Word/BD1_dados cadastrais e faturas.accdb;Jet OLEDB:Database Password=gds21"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user