Refatoração e reestruturação geral do projeto
Removidas classes, interfaces e testes obsoletos, incluindo `Application.cs`, `NF.cs`, `TERecord.cs`, `FileHandlerFactory.cs`, entre outros. A lógica de inicialização, manipulação de arquivos, validação de notas fiscais e acesso ao banco de dados foi completamente reestruturada ou substituída. Adicionada a classe `Arquivo` para encapsular informações de arquivos XML e PDF, com suporte a múltiplos PDFs associados a um único XML. Refatorada a classe `NF` para melhorar a organização e lidar com novos requisitos, como cálculo de valores e suporte a diferentes unidades de medida. A lógica de processamento de arquivos XML foi otimizada para execução paralela, com melhorias na geração de CSV e associação de arquivos. Configurações antigas, como `appsettings.json`, foram removidas. Dependências e namespaces desnecessários foram excluídos. Adicionados novos tratamentos de exceções, melhorias na modularidade e na clareza do código, além de suporte a novos padrões de saída e codificação. O projeto foi reorganizado para atender a novos requisitos e padrões.
This commit is contained in:
parent
14cdce64af
commit
3033a2e12a
@ -1,91 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
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 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
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}")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
namespace NfProcessorApp.Handlers
|
|
||||||
{
|
|
||||||
public interface IFileHandler
|
|
||||||
{
|
|
||||||
NFResult Process(string filePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
namespace NfProcessorApp.Handlers
|
|
||||||
{
|
|
||||||
public interface IFileHandlerFactory
|
|
||||||
{
|
|
||||||
IFileHandler CreateHandler(string filePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
namespace NfProcessorApp.Handlers
|
|
||||||
{
|
|
||||||
public interface IValidadorNF
|
|
||||||
{
|
|
||||||
bool Validate(NFResult result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
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 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
namespace NfProcessorApp.Handlers
|
|
||||||
{
|
|
||||||
public class PdfFileHandler : IFileHandler
|
|
||||||
{
|
|
||||||
public NFResult Process(string filePath)
|
|
||||||
{
|
|
||||||
// TODO: implementar leitura básica de PDF
|
|
||||||
return NFResult.Invalid(filePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,133 +0,0 @@
|
|||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
using NfProcessorApp.Handlers;
|
|
||||||
|
|
||||||
namespace NfProcessorApp.Infrastructure
|
|
||||||
{
|
|
||||||
public interface IDatabaseUpdater
|
|
||||||
{
|
|
||||||
void Update(NFResult result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -23,7 +23,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" 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="Moq" Version="4.20.72" />
|
||||||
<PackageReference Include="System.Data.OleDb" Version="10.0.0-preview.3.25171.5" />
|
<PackageReference Include="System.Data.OleDb" Version="10.0.0-preview.3.25171.5" />
|
||||||
<PackageReference Include="Unimake.DFe" Version="20250507.1733.30" />
|
<PackageReference Include="Unimake.DFe" Version="20250917.1627.18" />
|
||||||
<PackageReference Include="xunit" Version="2.9.3" />
|
<PackageReference Include="xunit" Version="2.9.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@ -42,10 +42,4 @@
|
|||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Update="appsettings.json">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
171
Program.cs
171
Program.cs
@ -1,34 +1,31 @@
|
|||||||
//namespace NfProcessorApp
|
using System.Reflection;
|
||||||
//{
|
|
||||||
// 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.Text;
|
||||||
using Unimake.Business.DFe.Xml.NFe;
|
using Unimake.Business.DFe.Xml.NFe;
|
||||||
|
using System.Data;
|
||||||
using Emit = Unimake.Business.DFe.Xml.NFe.Emit;
|
using Emit = Unimake.Business.DFe.Xml.NFe.Emit;
|
||||||
using System.Reflection;
|
using System.Xml;
|
||||||
|
using Unimake.Business.DFe.Validator.NFe;
|
||||||
|
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
|
static readonly string agora = DateTime.Now.AddMonths(-1).ToString("yyMM");
|
||||||
static async Task Main()
|
static async Task Main()
|
||||||
{
|
{
|
||||||
string pasta = @"X:\Back\Controle NFs\NFs";
|
string pasta = @"X:\Back\Controle NFs\NFs";
|
||||||
var arquivosXML = Directory.GetFiles(pasta, "*.Xml");
|
List<Arquivo> arquivosXML = Directory.GetFiles(pasta, "*.Xml").Select(x => new Arquivo(x)).Where(y => y.processar).ToList();
|
||||||
var arquivosPDF = Directory.GetFiles(pasta, "*.pdf");
|
foreach (var pdf in Directory.GetFiles(pasta, "*.pdf"))
|
||||||
var arquivos = arquivosXML.Concat(arquivosPDF);
|
{
|
||||||
|
if (arquivosXML.Any(x => x.Referencia == Path.GetFileName(pdf).Split(" - ").First()))
|
||||||
|
{
|
||||||
|
arquivosXML.First(x => x.Referencia == Path.GetFileName(pdf).Split(" - ").First())?.CaminhoPdf.Add(pdf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var arquivos = arquivosXML.OrderBy(x => x.DataDeCriacao);
|
||||||
var tarefas = new List<Task<string>>();
|
var tarefas = new List<Task<string>>();
|
||||||
var linhas = new List<string>();
|
var linhas = new List<string>();
|
||||||
|
|
||||||
//Processar arquivosXML em paralelo
|
//Processar arquivosXML em paralelo
|
||||||
foreach (string arquivo in arquivos)
|
foreach (var arquivo in arquivos)
|
||||||
{
|
{
|
||||||
tarefas.Add(ProcessarXMLAsync(arquivo));
|
tarefas.Add(ProcessarXMLAsync(arquivo));
|
||||||
//linhas.Add(await ProcessarXMLAsync(arquivo));
|
//linhas.Add(await ProcessarXMLAsync(arquivo));
|
||||||
@ -49,30 +46,61 @@ class Program
|
|||||||
Console.Write(csv.ToString());
|
Console.Write(csv.ToString());
|
||||||
//dados.Dispose();
|
//dados.Dispose();
|
||||||
}
|
}
|
||||||
static string ProcessarArquivo(string caminhoArquivo)
|
|
||||||
{
|
|
||||||
string nomeArquivo = Path.GetFileName(caminhoArquivo);
|
|
||||||
string tipoArquivo = Path.GetExtension(caminhoArquivo).TrimStart('.');
|
|
||||||
string referencia = nomeArquivo.Split(" - ").FirstOrDefault() ?? "";
|
|
||||||
|
|
||||||
// Formatar como CSV (com proteção contra vírgulas)
|
public class Arquivo
|
||||||
return $"\"{caminhoArquivo}\";\"{nomeArquivo}\";\"{tipoArquivo}\";\"{referencia}\"";
|
{
|
||||||
|
public string CaminhoArquivo { get; set; }
|
||||||
|
public List<string> CaminhoPdf { get; set; } = [];
|
||||||
|
public string NomeArquivo { get; set; }
|
||||||
|
public string TipoArquivo { get; set; }
|
||||||
|
public string Referencia { get; set; }
|
||||||
|
public DateTime DataDeCriacao { get; }
|
||||||
|
|
||||||
|
public NF NF;
|
||||||
|
public bool processar = true;
|
||||||
|
public Arquivo(string caminhoArquivo)
|
||||||
|
{
|
||||||
|
//Console.WriteLine($"Lendo {caminhoArquivo}");
|
||||||
|
if (caminhoArquivo == "X:\\Back\\Controle NFs\\NFs\\173420 - DANFE 000173420 (1).xml")
|
||||||
|
{
|
||||||
|
Thread.Sleep(10); // Simula atraso para testes
|
||||||
}
|
}
|
||||||
static async Task<string> ProcessarXMLAsync(string caminhoArquivo)
|
CaminhoArquivo = caminhoArquivo;
|
||||||
|
NomeArquivo = Path.GetFileName(caminhoArquivo);
|
||||||
|
TipoArquivo = Path.GetExtension(caminhoArquivo).TrimStart('.');
|
||||||
|
Referencia = NomeArquivo.Split(" - ").FirstOrDefault() ?? "";
|
||||||
|
DataDeCriacao = File.GetCreationTime(caminhoArquivo);
|
||||||
|
|
||||||
|
if (TipoArquivo.Equals("xml", StringComparison.CurrentCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NF = new NF(caminhoArquivo);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
processar = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
static async Task<string> ProcessarXMLAsync(Arquivo arquivo)
|
||||||
{
|
{
|
||||||
NFresponse response = new();
|
NFresponse response = new();
|
||||||
string status = "";
|
string status = "";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Path.GetExtension(caminhoArquivo).Equals(".Xml", StringComparison.CurrentCultureIgnoreCase))
|
if (arquivo.TipoArquivo.Equals("xml", StringComparison.CurrentCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
//if (caminhoArquivo == @"X:\Back\Controle NFs\NFs\345425911,145611 - NFe35250603984862000194550010001466521382788552-procNFe.xml")
|
//if (arquivo == @"X:\Back\Controle NFs\NFs\345425911,145611 - NFe35250603984862000194550010001466521382788552-procNFe.xml")
|
||||||
//{
|
//{
|
||||||
// Thread.Sleep(1000);
|
// Thread.Sleep(1000);
|
||||||
//}
|
//}
|
||||||
NF Current_NF = new(caminhoArquivo);
|
NF Current_NF = new(arquivo.CaminhoArquivo);
|
||||||
|
|
||||||
InfNFe infNFe = new NfeProc().LoadFromFile(caminhoArquivo).NFe.InfNFe.First();
|
InfNFe infNFe = new NfeProc().LoadFromFile(arquivo.CaminhoArquivo).NFe.InfNFe.First();
|
||||||
Dest Comprador = infNFe.Dest;
|
Dest Comprador = infNFe.Dest;
|
||||||
Emit Vendedor = infNFe.Emit;
|
Emit Vendedor = infNFe.Emit;
|
||||||
List<Det> Produtos = infNFe.Det;
|
List<Det> Produtos = infNFe.Det;
|
||||||
@ -114,6 +142,7 @@ class Program
|
|||||||
response.Numero_NF = Current_NF.Numero_NF ?? 0;
|
response.Numero_NF = Current_NF.Numero_NF ?? 0;
|
||||||
response.ICMS_NF = Current_NF.ICMS_NF;
|
response.ICMS_NF = Current_NF.ICMS_NF;
|
||||||
status = Current_NF.UF_NF ?? "";
|
status = Current_NF.UF_NF ?? "";
|
||||||
|
response.Month = Current_NF.Month ?? agora;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
@ -122,7 +151,7 @@ class Program
|
|||||||
}
|
}
|
||||||
return await Task.Run(() =>
|
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}\"";
|
return $"\"{arquivo.CaminhoArquivo}\";\"{arquivo.NomeArquivo}\";\"{arquivo.TipoArquivo}\";\"{arquivo.Referencia}\";\"{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}\";\"{arquivo.CaminhoPdf.Join("***")}\";\"{response.Month}\"";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public static double GetICMS(ICMS icms)
|
public static double GetICMS(ICMS icms)
|
||||||
@ -166,25 +195,27 @@ class Program
|
|||||||
public string RS_Vendedor { get; set; } = "";
|
public string RS_Vendedor { get; set; } = "";
|
||||||
public int Numero_NF { get; set; } = 0;
|
public int Numero_NF { get; set; } = 0;
|
||||||
public double ICMS_NF { get; set; } = 0;
|
public double ICMS_NF { get; set; } = 0;
|
||||||
|
public string Month { get; set; } = "";
|
||||||
}
|
}
|
||||||
public class NF
|
public class NF
|
||||||
(
|
|
||||||
string caminhoArquivo
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
readonly InfNFe infNFe = new NfeProc().LoadFromFile(caminhoArquivo).NFe.InfNFe.First();
|
public NF(string caminhoArquivo)
|
||||||
Dest Comprador => infNFe.Dest;
|
{
|
||||||
Emit Vendedor => infNFe.Emit;
|
nfeProc = new NfeProc().LoadFromFile(caminhoArquivo);
|
||||||
Det Detalhes => infNFe.Det.First();
|
nfe = nfeProc.NFe;
|
||||||
List<Det> Produtos => infNFe.Det;
|
infNFe = nfe.InfNFe.FirstOrDefault() ?? throw new InvalidOperationException("InfNFe is null or empty.");
|
||||||
Imposto Impostos => Detalhes.Imposto;
|
Comprador = infNFe.Dest;
|
||||||
public string NomeArquivo => Path.GetFileName(caminhoArquivo);
|
Vendedor = infNFe.Emit;
|
||||||
public string? TipoArquivo => Path.GetExtension(caminhoArquivo).TrimStart('.');
|
Detalhes = infNFe.Det.First();
|
||||||
public string? Referencia => this.NomeArquivo.Split(" - ").FirstOrDefault() ?? "";
|
Produtos = infNFe.Det;
|
||||||
public string CNPJ_Comprador => Convert.ToInt64(Comprador.CNPJ ?? Comprador.CPF).ToString(@"00000000000000");
|
Impostos = Detalhes.Imposto;
|
||||||
public string CNPJ_Vendedor => Convert.ToInt64(Vendedor.CNPJ).ToString(@"00000000000000");
|
NomeArquivo = Path.GetFileName(caminhoArquivo);
|
||||||
|
TipoArquivo = Path.GetExtension(caminhoArquivo).TrimStart('.');
|
||||||
|
Referencia = NF.NomeArquivo.Split(" - ").FirstOrDefault() ?? "";
|
||||||
|
CNPJ_Comprador = Convert.ToInt64(Comprador.CNPJ ?? Comprador.CPF).ToString(@"00000000000000");
|
||||||
|
CNPJ_Vendedor = Convert.ToInt64(Vendedor.CNPJ).ToString(@"00000000000000");
|
||||||
|
|
||||||
public decimal Soma2 =>
|
Soma2 =
|
||||||
Produtos.Sum(prod =>
|
Produtos.Sum(prod =>
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
@ -201,8 +232,8 @@ class Program
|
|||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
public decimal Montante_operação
|
Montante_operação
|
||||||
=>
|
=
|
||||||
Produtos.Sum(prod =>
|
Produtos.Sum(prod =>
|
||||||
(
|
(
|
||||||
prod.Prod.UCom == "KWH" ?
|
prod.Prod.UCom == "KWH" ?
|
||||||
@ -210,7 +241,7 @@ class Program
|
|||||||
prod.Prod.QCom
|
prod.Prod.QCom
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
public decimal Valor_unitário =>
|
Valor_unitário =
|
||||||
(
|
(
|
||||||
Comprador.EnderDest.UF.ToString() == "SP" &&
|
Comprador.EnderDest.UF.ToString() == "SP" &&
|
||||||
Vendedor.EnderEmit.UF.ToString() == "SP"
|
Vendedor.EnderEmit.UF.ToString() == "SP"
|
||||||
@ -230,12 +261,38 @@ class Program
|
|||||||
:
|
:
|
||||||
(Soma2 / Montante_operação)
|
(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)));
|
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("&", "&");
|
RS_Comprador = Comprador.XNome.Replace("&amp;", "&").Replace("&", "&");
|
||||||
public string? RS_Vendedor => Vendedor.XNome.Replace("&amp;", "&").Replace("&", "&");
|
RS_Vendedor = Vendedor.XNome.Replace("&amp;", "&").Replace("&", "&");
|
||||||
public int? Numero_NF => infNFe.Ide.NNF;
|
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);
|
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();
|
UF_NF = Comprador.EnderDest.UF.ToString();
|
||||||
public string? UF_Vend => Vendedor.EnderEmit.UF.ToString();
|
UF_Vend = Vendedor.EnderEmit.UF.ToString();
|
||||||
|
Month = infNFe.Ide.DhEmi.AddMonths(-1).ToString("yyMM");
|
||||||
|
}
|
||||||
|
static NfeProc nfeProc;
|
||||||
|
static NFe nfe;
|
||||||
|
static InfNFe infNFe;
|
||||||
|
static Dest Comprador;
|
||||||
|
static Emit Vendedor;
|
||||||
|
static Det Detalhes;
|
||||||
|
static List<Det> Produtos;
|
||||||
|
static Imposto Impostos;
|
||||||
|
public static string NomeArquivo;
|
||||||
|
public static string? TipoArquivo;
|
||||||
|
public string? Referencia;
|
||||||
|
public string CNPJ_Comprador;
|
||||||
|
public string CNPJ_Vendedor;
|
||||||
|
public decimal Soma2;
|
||||||
|
public decimal Montante_operação;
|
||||||
|
public decimal Valor_unitário;
|
||||||
|
public double Valor_final_c_impostos;
|
||||||
|
public string? RS_Comprador;
|
||||||
|
public string? RS_Vendedor;
|
||||||
|
public int? Numero_NF;
|
||||||
|
public double ICMS_NF;
|
||||||
|
public string? UF_NF;
|
||||||
|
public string? UF_Vend;
|
||||||
|
public string? Month;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,13 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
namespace NfProcessorApp.Services
|
|
||||||
{
|
|
||||||
public interface IFileScanner
|
|
||||||
{
|
|
||||||
IEnumerable<string> ListFiles(string folderPath, string[] extensions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"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