Import inicial: migração de arquivos da rede
This commit is contained in:
parent
ad7dc587b2
commit
c48ba4710d
41
AuditComplianceNFs.cs
Normal file
41
AuditComplianceNFs.cs
Normal file
@ -0,0 +1,41 @@
|
||||
// 📋 Copilot Audit Prompt: ComplianceNFs Project Validator
|
||||
//
|
||||
// Purpose:
|
||||
// Automatically review the ComplianceNFs solution and report whether
|
||||
// each major requirement from the initial design prompt is implemented.
|
||||
//
|
||||
// Instructions to Copilot:
|
||||
// Write a C# console app or NUnit/xUnit test suite that:
|
||||
// 1. Loads the ComplianceNFs solution structure (projects: .Service, .Monitor, .Core, .Infrastructure).
|
||||
// 2. Verifies that each project exists and follows naming conventions.
|
||||
// 3. In ComplianceNFs.Core:
|
||||
// • Confirms presence of BuyingRecord, EnergyInvoice, InvoiceStatus enum.
|
||||
// • Confirms interfaces IMailListener, IXmlParser, IPdfParser, IAccessDbRepository, IAttachmentRepository.
|
||||
// 4. In ComplianceNFs.Infrastructure:
|
||||
// • Checks for classes implementing each interface (AccessDbRepository, AttachmentRepository, MailListener, XmlParser, PdfParser).
|
||||
// • Confirms a FileArchiver or equivalent that creates status subfolders under ArchiveBasePath.
|
||||
// 5. In ComplianceNFs.Service:
|
||||
// • Confirms a Generic Host (`Host.CreateDefaultBuilder`) and DI registration for all services.
|
||||
// • Asserts that MailListener is started on host startup.
|
||||
// • Reads configuration from appsettings.json (AccessConnectionString, PostgresConnectionString, Mail settings, Tolerances, ArchiveBasePath).
|
||||
// 6. In ComplianceNFs.Monitor:
|
||||
// • Checks for a WPF application with a MainWindow, MonitorViewModel, and a “Force Scan” button wired to trigger ingestion.
|
||||
// 7. Workflow & Logic Checks:
|
||||
// • Ensures MatchingService applies primary key match (CNPJ_comp, CNPJ_vend, MontLO vs MontNF ±1%, PrecLO vs PrecNF ±0.5%).
|
||||
// • Ensures fallback-by-date logic (invoiceDate minus 1 month → match on Mes/Ano).
|
||||
// • Ensures multi-invoice sum check for volume matches.
|
||||
// • Ensures ComplianceService computes implied tax and compares within ±1%.
|
||||
// • Ensures NotificationService sends email on mismatches.
|
||||
// • Ensures ArchivingService moves files into subfolders named for each InvoiceStatus.
|
||||
// 8. Configuration Validations:
|
||||
// • Reads appsettings.json and asserts each required key exists.
|
||||
// 9. Produces a summary report (console output or test results) listing “Pass” or “Fail” for each check,
|
||||
// and points to the file/line where a missing or mis-configured item was detected.
|
||||
//
|
||||
// Approach:
|
||||
// • Use reflection or simple file parsing (e.g. Roslyn) to find classes, interfaces, methods.
|
||||
// • Use JSON parsing to read and validate appsettings.json.
|
||||
// • Optionally scaffold unit tests that assert the numeric tolerances and fallback logic via small in-memory examples.
|
||||
// • Output a clear, human-readable checklist with file paths and suggestions for fixes.
|
||||
//
|
||||
// Begin generating the auditing code now.
|
||||
@ -5,6 +5,7 @@ namespace ComplianceNFs.Core.Entities
|
||||
{
|
||||
public class EnergyInvoice
|
||||
{
|
||||
[System.ComponentModel.DataAnnotations.Key]
|
||||
public required string MailId { get; set; }
|
||||
public required string ConversationId { get; set; }
|
||||
public required string SupplierEmail { get; set; }
|
||||
|
||||
@ -6,6 +6,7 @@ using Xunit;
|
||||
using Moq;
|
||||
using Npgsql;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ComplianceNFs.Infrastructure.Tests
|
||||
{
|
||||
@ -15,8 +16,11 @@ namespace ComplianceNFs.Infrastructure.Tests
|
||||
public async Task SaveRawAsync_DoesNotThrow_WithValidInvoice()
|
||||
{
|
||||
// Arrange
|
||||
var options = new DbContextOptionsBuilder<ComplianceNFsDbContext>()
|
||||
.Options;
|
||||
using var dbContext = new ComplianceNFsDbContext(options);
|
||||
var mockLogger = new Mock<ILogger<AttachmentRepository>>();
|
||||
var repo = new AttachmentRepository("Host=localhost;Port=5432;Database=test;Username=test;Password=test", mockLogger.Object);
|
||||
var repo = new AttachmentRepository(dbContext, mockLogger.Object);
|
||||
var invoice = new EnergyInvoice
|
||||
{
|
||||
MailId = "mailid",
|
||||
@ -27,9 +31,8 @@ namespace ComplianceNFs.Infrastructure.Tests
|
||||
Filename = "file.xml",
|
||||
Status = InvoiceStatus.Validated
|
||||
};
|
||||
// This is a placeholder: in a real test, use a test DB or mock NpgsqlConnection/Command
|
||||
// For demonstration, we'll just check that the method can be called without throwing
|
||||
await Assert.ThrowsAnyAsync<Exception>(async () => await repo.SaveRawAsync(invoice));
|
||||
// Act & Assert
|
||||
await repo.SaveRawAsync(invoice); // Should not throw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
151
ComplianceNFs.Infrastructure.Tests/AuditComplianceNFsTest.cs
Normal file
151
ComplianceNFs.Infrastructure.Tests/AuditComplianceNFsTest.cs
Normal file
@ -0,0 +1,151 @@
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ComplianceNFs.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class AuditComplianceNFsTest
|
||||
{
|
||||
private readonly string solutionRoot = @"x:\Back\Carteira x.x\Codigo\ComplianceNFs";
|
||||
|
||||
[Test]
|
||||
public void ProjectStructure_ShouldContainAllProjects()
|
||||
{
|
||||
var expectedProjects = new[]
|
||||
{
|
||||
"ComplianceNFs.Core",
|
||||
"ComplianceNFs.Infrastructure",
|
||||
"ComplianceNFs.Service",
|
||||
"ComplianceNFs.Monitor"
|
||||
};
|
||||
foreach (var proj in expectedProjects)
|
||||
{
|
||||
var csproj = Directory.GetFiles(solutionRoot, $"{proj}.csproj", SearchOption.AllDirectories).FirstOrDefault();
|
||||
NUnit.Framework.Assert.That(csproj, Is.Not.Null, $"Project {proj} not found.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CoreProject_ShouldContainRequiredTypesAndInterfaces()
|
||||
{
|
||||
var coreDll = Directory.GetFiles(solutionRoot, "ComplianceNFs.Core.dll", SearchOption.AllDirectories).FirstOrDefault();
|
||||
NUnit.Framework.Assert.That(coreDll, NUnit.Framework.Is.Not.Null, "ComplianceNFs.Core.dll not found. Build the solution first.");
|
||||
var asm = Assembly.LoadFrom(coreDll!);
|
||||
NUnit.Framework.Assert.That(asm.GetType("ComplianceNFs.Core.BuyingRecord"), NUnit.Framework.Is.Not.Null, "BuyingRecord class missing.");
|
||||
NUnit.Framework.Assert.That(asm.GetType("ComplianceNFs.Core.EnergyInvoice"), NUnit.Framework.Is.Not.Null, "EnergyInvoice class missing.");
|
||||
NUnit.Framework.Assert.That(asm.GetType("ComplianceNFs.Core.InvoiceStatus"), NUnit.Framework.Is.Not.Null, "InvoiceStatus enum missing.");
|
||||
var interfaces = new[]
|
||||
{
|
||||
"IMailListener",
|
||||
"IXmlParser",
|
||||
"IPdfParser",
|
||||
"IAccessDbRepository",
|
||||
"IAttachmentRepository"
|
||||
};
|
||||
foreach (var iface in interfaces)
|
||||
{
|
||||
NUnit.Framework.Assert.That(asm.GetType($"ComplianceNFs.Core.{iface}"), NUnit.Framework.Is.Not.Null, $"Interface {iface} missing.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InfrastructureProject_ShouldImplementCoreInterfaces()
|
||||
{
|
||||
var infraDll = Directory.GetFiles(solutionRoot, "ComplianceNFs.Infrastructure.dll", SearchOption.AllDirectories).FirstOrDefault();
|
||||
var coreDll = Directory.GetFiles(solutionRoot, "ComplianceNFs.Core.dll", SearchOption.AllDirectories).FirstOrDefault();
|
||||
NUnit.Framework.Assert.That(infraDll, NUnit.Framework.Is.Not.Null, "ComplianceNFs.Infrastructure.dll not found.");
|
||||
NUnit.Framework.Assert.That(coreDll, NUnit.Framework.Is.Not.Null, "ComplianceNFs.Core.dll not found.");
|
||||
var infraAsm = Assembly.LoadFrom(infraDll!);
|
||||
var coreAsm = Assembly.LoadFrom(coreDll!);
|
||||
var interfaceImpls = new (string iface, string impl)[]
|
||||
{
|
||||
("IMailListener", "MailListener"),
|
||||
("IXmlParser", "XmlParser"),
|
||||
("IPdfParser", "PdfParser"),
|
||||
("IAccessDbRepository", "AccessDbRepository"),
|
||||
("IAttachmentRepository", "AttachmentRepository")
|
||||
};
|
||||
foreach (var (iface, impl) in interfaceImpls)
|
||||
{
|
||||
var ifaceType = coreAsm.GetType($"ComplianceNFs.Core.{iface}");
|
||||
var implType = infraAsm.GetType($"ComplianceNFs.Infrastructure.{impl}");
|
||||
NUnit.Framework.Assert.That(implType, NUnit.Framework.Is.Not.Null, $"Implementation {impl} missing.");
|
||||
NUnit.Framework.Assert.That(ifaceType, NUnit.Framework.Is.Not.Null, $"Interface {iface} missing in core assembly.");
|
||||
NUnit.Framework.Assert.That(ifaceType!.IsAssignableFrom(implType!), NUnit.Framework.Is.True, $"{impl} does not implement {iface}.");
|
||||
}
|
||||
var archiver = infraAsm.GetTypes().FirstOrDefault(t =>
|
||||
t.Name.Contains("Archiver") || t.Name.Contains("FileArchiver"));
|
||||
NUnit.Framework.Assert.That(archiver, NUnit.Framework.Is.Not.Null, "FileArchiver or equivalent not found.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ServiceProject_ShouldUseGenericHostAndRegisterServices()
|
||||
{
|
||||
var serviceDir = Directory.GetDirectories(solutionRoot, "ComplianceNFs.Service", SearchOption.TopDirectoryOnly).FirstOrDefault();
|
||||
NUnit.Framework.Assert.That(serviceDir, NUnit.Framework.Is.Not.Null, "ComplianceNFs.Service directory not found.");
|
||||
var programFile = Directory.GetFiles(serviceDir!, "Program.cs", SearchOption.AllDirectories).FirstOrDefault();
|
||||
NUnit.Framework.Assert.That(programFile, NUnit.Framework.Is.Not.Null, "Program.cs not found in Service project.");
|
||||
var code = File.ReadAllText(programFile!);
|
||||
NUnit.Framework.Assert.That(code.Contains("Host.CreateDefaultBuilder"), NUnit.Framework.Is.True, "Generic Host not used.");
|
||||
NUnit.Framework.Assert.That(code.Contains("ConfigureServices"), NUnit.Framework.Is.True, "DI registration missing.");
|
||||
NUnit.Framework.Assert.That(code.Contains("MailListener"), NUnit.Framework.Is.True, "MailListener not referenced in startup.");
|
||||
NUnit.Framework.Assert.That(code.Contains("appsettings.json"), NUnit.Framework.Is.True, "appsettings.json not referenced.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MonitorProject_ShouldContainWPFArtifacts()
|
||||
{
|
||||
var monitorDir = Directory.GetDirectories(solutionRoot, "ComplianceNFs.Monitor", SearchOption.TopDirectoryOnly).FirstOrDefault();
|
||||
NUnit.Framework.Assert.That(monitorDir, NUnit.Framework.Is.Not.Null, "ComplianceNFs.Monitor directory not found.");
|
||||
NUnit.Framework.Assert.That(File.Exists(Path.Combine(monitorDir!, "MainWindow.xaml")), NUnit.Framework.Is.True, "MainWindow.xaml missing.");
|
||||
NUnit.Framework.Assert.That(File.Exists(Path.Combine(monitorDir!, "MonitorViewModel.cs")), NUnit.Framework.Is.True, "MonitorViewModel.cs missing.");
|
||||
var mainWindowCode = File.ReadAllText(Path.Combine(monitorDir!, "MainWindow.xaml"));
|
||||
NUnit.Framework.Assert.That(mainWindowCode.Contains("Force Scan"), NUnit.Framework.Is.True, "Force Scan button not found in MainWindow.xaml.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AppSettings_ShouldContainRequiredKeys()
|
||||
{
|
||||
var appsettings = Directory.GetFiles(solutionRoot, "appsettings.json", SearchOption.AllDirectories).FirstOrDefault();
|
||||
NUnit.Framework.Assert.That(appsettings, NUnit.Framework.Is.Not.Null, "appsettings.json not found.");
|
||||
var json = File.ReadAllText(appsettings!);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
NUnit.Framework.Assert.That(root.TryGetProperty("AccessConnectionString", out _), NUnit.Framework.Is.True, "AccessConnectionString missing.");
|
||||
NUnit.Framework.Assert.That(root.TryGetProperty("PostgresConnectionString", out _), NUnit.Framework.Is.True, "PostgresConnectionString missing.");
|
||||
NUnit.Framework.Assert.That(root.TryGetProperty("Mail", out _), NUnit.Framework.Is.True, "Mail settings missing.");
|
||||
NUnit.Framework.Assert.That(root.TryGetProperty("Tolerances", out _), NUnit.Framework.Is.True, "Tolerances missing.");
|
||||
NUnit.Framework.Assert.That(root.TryGetProperty("ArchiveBasePath", out _), NUnit.Framework.Is.True, "ArchiveBasePath missing.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MatchingService_ShouldApplyTolerances()
|
||||
{
|
||||
// This is a stub: in a real test, instantiate MatchingService and test with in-memory data.
|
||||
NUnit.Framework.Assert.Pass("Stub: Implement in-memory test for MatchingService tolerances (±1%, ±0.5%).");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComplianceService_ShouldComputeImpliedTaxWithinTolerance()
|
||||
{
|
||||
// This is a stub: in a real test, instantiate ComplianceService and test with in-memory data.
|
||||
NUnit.Framework.Assert.Pass("Stub: Implement in-memory test for ComplianceService implied tax logic (±1%).");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NotificationService_ShouldSendEmailOnMismatch()
|
||||
{
|
||||
// This is a stub: in a real test, mock NotificationService and verify email send on mismatch.
|
||||
NUnit.Framework.Assert.Pass("Stub: Implement NotificationService email send test.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ArchivingService_ShouldMoveFilesToStatusFolders()
|
||||
{
|
||||
// This is a stub: in a real test, mock file system and verify archiving logic.
|
||||
NUnit.Framework.Assert.Pass("Stub: Implement ArchivingService file move test.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0-preview.4.25258.110" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="NUnit" Version="4.3.2" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -13,8 +13,8 @@ namespace ComplianceNFs.Infrastructure.Tests
|
||||
public FileArchiverTests()
|
||||
{
|
||||
_testBasePath = Path.Combine(Path.GetTempPath(), "ComplianceNFsTestArchive");
|
||||
if (Directory.Exists(_testBasePath))
|
||||
Directory.Delete(_testBasePath, true);
|
||||
// if (Directory.Exists(_testBasePath))
|
||||
// Directory.Delete(_testBasePath, true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -26,18 +26,18 @@ namespace ComplianceNFs.Infrastructure.Tests
|
||||
MailId = "test-mail-id",
|
||||
ConversationId = "test-conv-id",
|
||||
SupplierEmail = "test@supplier.com",
|
||||
Filename = "testfile.txt",
|
||||
Filename = @"X:\Back\Controle NFs\NFs\temp\99451152,9268928 - procNFE52250318384740000134550020000065021906003771.xml",
|
||||
Status = InvoiceStatus.Validated,
|
||||
// Add required fields for null safety
|
||||
ReceivedDate = DateTime.Now,
|
||||
InvoiceId = 1
|
||||
};
|
||||
var data = new byte[] { 1, 2, 3, 4 };
|
||||
var data = await File.ReadAllBytesAsync(invoice.Filename);
|
||||
|
||||
archiver.ArchiveAsync(invoice);
|
||||
|
||||
var expectedFolder = Path.Combine(_testBasePath, "Validated");
|
||||
var expectedFile = Path.Combine(expectedFolder, "testfile.txt");
|
||||
var expectedFile = Path.Combine(expectedFolder, @"X:\Back\Controle NFs\NFs\temp\99451152,9268928 - procNFE52250318384740000134550020000065021906003771.xml");
|
||||
Assert.True(Directory.Exists(expectedFolder));
|
||||
Assert.True(File.Exists(expectedFile));
|
||||
var fileData = await File.ReadAllBytesAsync(expectedFile);
|
||||
@ -53,18 +53,18 @@ namespace ComplianceNFs.Infrastructure.Tests
|
||||
MailId = "test-mail-id",
|
||||
ConversationId = "test-conv-id",
|
||||
SupplierEmail = "test@supplier.com",
|
||||
Filename = "testfile.txt",
|
||||
Filename = @"X:\Back\Controle NFs\NFs\temp\99451152,9268928 - procNFE52250318384740000134550020000065021906003771.xml",
|
||||
Status = InvoiceStatus.Validated,
|
||||
// Add required fields for null safety
|
||||
ReceivedDate = DateTime.Now,
|
||||
InvoiceId = 1
|
||||
};
|
||||
var data2 = new byte[] { 9, 8, 7 };
|
||||
var data2 = await File.ReadAllBytesAsync(invoice.Filename);
|
||||
|
||||
archiver.ArchiveAsync(invoice);
|
||||
archiver.ArchiveAsync(invoice);
|
||||
|
||||
var expectedFile = Path.Combine(_testBasePath, "Validated", "testfile.txt");
|
||||
var expectedFile = Path.Combine(_testBasePath, "Validated", @"X:\Back\Controle NFs\NFs\temp\99451152,9268928 - procNFE52250318384740000134550020000065021906003771.xml");
|
||||
var fileData = await File.ReadAllBytesAsync(expectedFile);
|
||||
Assert.Equal(data2, fileData);
|
||||
}
|
||||
|
||||
@ -6,12 +6,19 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MailKit" Version="4.12.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0-preview.4.25258.110" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.0-preview.4.25258.110" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.0-preview.4.25258.110" />
|
||||
<PackageReference Include="Microsoft.Office.Interop.Outlook" Version="15.0.4797.1004" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Npgsql" Version="9.0.3" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageReference Include="System.Data.OleDb" Version="10.0.0-preview.4.25258.110" />
|
||||
<PackageReference Include="Unimake.DFe" Version="20250610.1145.39" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -6,51 +6,32 @@ using Npgsql;
|
||||
using Newtonsoft.Json;
|
||||
using System.Numerics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ComplianceNFs.Infrastructure.Repositories
|
||||
{
|
||||
// Placeholder: fill in actual SQL and mapping logic
|
||||
public class AttachmentRepository(string connectionString, ILogger<AttachmentRepository> logger) : IAttachmentRepository
|
||||
public class AttachmentRepository : IAttachmentRepository
|
||||
{
|
||||
private readonly ComplianceNFsDbContext _dbContext;
|
||||
private readonly ILogger<AttachmentRepository> _logger;
|
||||
|
||||
public AttachmentRepository(ComplianceNFsDbContext dbContext, ILogger<AttachmentRepository> logger)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task SaveRawAsync(EnergyInvoice invoice)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var conn = new NpgsqlConnection(connectionString);
|
||||
await conn.OpenAsync();
|
||||
var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = @"INSERT INTO attachments (
|
||||
filename, supplier_email, conversation_id, received_date, md5, cnpj_comp, cnpj_vend, mont_nf, prec_nf, valor_sem_imp, valor_com_imp, rs_comp, rs_vend, numero_nf, icms_nf, uf_comp, uf_vend, matched_cod_te, status, discrepancy, metadata
|
||||
) VALUES (
|
||||
@filename, @supplier_email, @conversation_id, @received_date, @md5, @cnpj_comp, @cnpj_vend, @mont_nf, @prec_nf, @valor_sem_imp, @valor_com_imp, @rs_comp, @rs_vend, @numero_nf, @icms_nf, @uf_comp, @uf_vend, @matched_cod_te, @status, @discrepancy, @metadata
|
||||
)";
|
||||
cmd.Parameters.AddWithValue("@filename", invoice.Filename);
|
||||
cmd.Parameters.AddWithValue("@supplier_email", invoice.SupplierEmail);
|
||||
cmd.Parameters.AddWithValue("@conversation_id", (object?)invoice.ConversationId ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@received_date", invoice.ReceivedDate);
|
||||
cmd.Parameters.AddWithValue("@md5", (object?)invoice.Md5 ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@cnpj_comp", (object?)invoice.CnpjComp ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@cnpj_vend", (object?)invoice.CnpjVend ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@mont_nf", (object?)invoice.MontNF ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@prec_nf", (object?)invoice.PrecNF ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@valor_sem_imp", (object?)invoice.ValorSemImpostos ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@valor_com_imp", (object?)invoice.ValorFinalComImpostos ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@rs_comp", (object?)invoice.RsComp ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@rs_vend", (object?)invoice.RsVend ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@numero_nf", (object?)invoice.NumeroNF ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@icms_nf", (object?)invoice.IcmsNF ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@uf_comp", (object?)invoice.UfComp ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@uf_vend", (object?)invoice.UfVend ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@matched_cod_te", (object?)invoice.MatchedCodTE ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@status", invoice.Status.ToString());
|
||||
cmd.Parameters.AddWithValue("@discrepancy", (object?)invoice.DiscrepancyNotes ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@metadata", Newtonsoft.Json.JsonConvert.SerializeObject(invoice));
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
logger.LogInformation("Saved raw invoice {InvoiceId} to attachments table.", invoice.InvoiceId);
|
||||
await _dbContext.EnergyInvoices.AddAsync(invoice);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
_logger.LogInformation("Saved raw invoice {InvoiceId} to EnergyInvoices table.", invoice.InvoiceId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error saving raw invoice {InvoiceId} to attachments table.", invoice.InvoiceId);
|
||||
_logger.LogError(ex, "Error saving raw invoice {InvoiceId} to EnergyInvoices table.", invoice.InvoiceId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@ -59,20 +40,20 @@ namespace ComplianceNFs.Infrastructure.Repositories
|
||||
{
|
||||
try
|
||||
{
|
||||
using var conn = new NpgsqlConnection(connectionString);
|
||||
await conn.OpenAsync();
|
||||
var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = @"UPDATE attachments SET matched_cod_te = @matched_cod_te, status = @status, discrepancy = @discrepancy WHERE invoice_id = @invoice_id";
|
||||
cmd.Parameters.AddWithValue("@matched_cod_te", matchedCodTE);
|
||||
cmd.Parameters.AddWithValue("@status", status.ToString());
|
||||
cmd.Parameters.AddWithValue("@discrepancy", (object?)notes ?? DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@invoice_id", invoiceId);
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
logger.LogInformation("Updated match for invoice {InvoiceId}.", invoiceId);
|
||||
var invoice = await _dbContext.EnergyInvoices.FirstOrDefaultAsync(e => e.InvoiceId == invoiceId);
|
||||
if (invoice == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Invoice with ID {invoiceId} not found.");
|
||||
}
|
||||
invoice.MatchedCodTE = matchedCodTE;
|
||||
invoice.Status = status;
|
||||
invoice.DiscrepancyNotes = notes;
|
||||
await _dbContext.SaveChangesAsync();
|
||||
_logger.LogInformation("Updated match for invoice {InvoiceId}.", invoiceId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error updating match for invoice {InvoiceId}.", invoiceId);
|
||||
_logger.LogError(ex, "Error updating match for invoice {InvoiceId}.", invoiceId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ComplianceNFs.Core.Entities;
|
||||
|
||||
namespace ComplianceNFs.Infrastructure.Repositories
|
||||
{
|
||||
public class ComplianceNFsDbContext : DbContext
|
||||
{
|
||||
public ComplianceNFsDbContext(DbContextOptions<ComplianceNFsDbContext> options) : base(options) { }
|
||||
|
||||
public DbSet<EnergyInvoice> EnergyInvoices { get; set; }
|
||||
// Add other DbSets as needed (e.g., BuyingRecord, etc.)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.IO;
|
||||
|
||||
namespace ComplianceNFs.Infrastructure.Repositories
|
||||
{
|
||||
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<ComplianceNFsDbContext>
|
||||
{
|
||||
public ComplianceNFsDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
// Build config to read connection string from appsettings.json
|
||||
var config = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.json", optional: true)
|
||||
.Build();
|
||||
|
||||
var connectionString = config["PostgresConnectionString"] ??
|
||||
"Host=localhost;Database=compliancenfs;Username=postgres;Password=postgres";
|
||||
|
||||
var optionsBuilder = new DbContextOptionsBuilder<ComplianceNFsDbContext>();
|
||||
optionsBuilder.UseNpgsql(connectionString);
|
||||
|
||||
return new ComplianceNFsDbContext(optionsBuilder.Options);
|
||||
}
|
||||
}
|
||||
}
|
||||
107
ComplianceNFs.Infrastructure/Repositories/Migrations/20250704165647_InitialCreate.Designer.cs
generated
Normal file
107
ComplianceNFs.Infrastructure/Repositories/Migrations/20250704165647_InitialCreate.Designer.cs
generated
Normal file
@ -0,0 +1,107 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using ComplianceNFs.Infrastructure.Repositories;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ComplianceNFs.Infrastructure.Repositories.Migrations
|
||||
{
|
||||
[DbContext(typeof(ComplianceNFsDbContext))]
|
||||
[Migration("20250704165647_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.6")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("ComplianceNFs.Core.Entities.EnergyInvoice", b =>
|
||||
{
|
||||
b.Property<string>("MailId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("CnpjComp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("CnpjVend")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ConversationId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("DiscrepancyNotes")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Filename")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal?>("IcmsNF")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<int>("InvoiceId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<BigInteger?>("MatchedCodTE")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<string>("Md5")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal?>("MontNF")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<string>("NumeroNF")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal?>("PrecNF")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<DateTime>("ReceivedDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("RsComp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RsVend")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("SupplierEmail")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UfComp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UfVend")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal?>("ValorFinalComImpostos")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("ValorSemImpostos")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.HasKey("MailId");
|
||||
|
||||
b.ToTable("EnergyInvoices");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ComplianceNFs.Infrastructure.Repositories.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EnergyInvoices",
|
||||
columns: table => new
|
||||
{
|
||||
MailId = table.Column<string>(type: "text", nullable: false),
|
||||
ConversationId = table.Column<string>(type: "text", nullable: false),
|
||||
SupplierEmail = table.Column<string>(type: "text", nullable: false),
|
||||
ReceivedDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
InvoiceId = table.Column<int>(type: "integer", nullable: false),
|
||||
Filename = table.Column<string>(type: "text", nullable: false),
|
||||
Md5 = table.Column<string>(type: "text", nullable: true),
|
||||
CnpjComp = table.Column<string>(type: "text", nullable: true),
|
||||
CnpjVend = table.Column<string>(type: "text", nullable: true),
|
||||
MontNF = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
PrecNF = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
ValorSemImpostos = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
ValorFinalComImpostos = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
RsComp = table.Column<string>(type: "text", nullable: true),
|
||||
RsVend = table.Column<string>(type: "text", nullable: true),
|
||||
NumeroNF = table.Column<string>(type: "text", nullable: true),
|
||||
IcmsNF = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
UfComp = table.Column<string>(type: "text", nullable: true),
|
||||
UfVend = table.Column<string>(type: "text", nullable: true),
|
||||
MatchedCodTE = table.Column<BigInteger>(type: "numeric", nullable: true),
|
||||
Status = table.Column<int>(type: "integer", nullable: false),
|
||||
DiscrepancyNotes = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EnergyInvoices", x => x.MailId);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EnergyInvoices");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using ComplianceNFs.Infrastructure.Repositories;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ComplianceNFs.Infrastructure.Repositories.Migrations
|
||||
{
|
||||
[DbContext(typeof(ComplianceNFsDbContext))]
|
||||
partial class ComplianceNFsDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.6")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("ComplianceNFs.Core.Entities.EnergyInvoice", b =>
|
||||
{
|
||||
b.Property<string>("MailId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("CnpjComp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("CnpjVend")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ConversationId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("DiscrepancyNotes")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Filename")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal?>("IcmsNF")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<int>("InvoiceId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<BigInteger?>("MatchedCodTE")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<string>("Md5")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal?>("MontNF")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<string>("NumeroNF")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal?>("PrecNF")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<DateTime>("ReceivedDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("RsComp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RsVend")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("SupplierEmail")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UfComp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UfVend")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal?>("ValorFinalComImpostos")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("ValorSemImpostos")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.HasKey("MailId");
|
||||
|
||||
b.ToTable("EnergyInvoices");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.0-preview.4.25258.110" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-preview.4.25258.110" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.0-preview.4.25258.110" />
|
||||
|
||||
@ -9,6 +9,8 @@ using ComplianceNFs.Infrastructure.Parsers;
|
||||
using ComplianceNFs.Infrastructure.Archiving;
|
||||
using ComplianceNFs.Core.Application.Services;
|
||||
using ComplianceNFs.Core.Application;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ComplianceNFs.Infrastructure;
|
||||
|
||||
IHost host = Host.CreateDefaultBuilder(args)
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
@ -18,6 +20,9 @@ IHost host = Host.CreateDefaultBuilder(args)
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
var config = context.Configuration;
|
||||
// Register EF Core DbContext for PostgreSQL
|
||||
services.AddDbContext<ComplianceNFsDbContext>(options =>
|
||||
options.UseNpgsql(config["PostgresConnectionString"]));
|
||||
// Register infrastructure
|
||||
services.AddSingleton<IAccessDbRepository>(sp =>
|
||||
{
|
||||
@ -26,14 +31,8 @@ IHost host = Host.CreateDefaultBuilder(args)
|
||||
throw new InvalidOperationException("AccessConnectionString is missing in configuration.");
|
||||
return new AccessDbRepository(connectionString);
|
||||
});
|
||||
services.AddSingleton<IAttachmentRepository>(sp =>
|
||||
{
|
||||
var pgConnectionString = config["PostgresConnectionString"];
|
||||
if (string.IsNullOrWhiteSpace(pgConnectionString))
|
||||
throw new InvalidOperationException("PostgresConnectionString is missing in configuration.");
|
||||
var logger = sp.GetRequiredService<Microsoft.Extensions.Logging.ILogger<AttachmentRepository>>();
|
||||
return new AttachmentRepository(pgConnectionString, logger);
|
||||
});
|
||||
// Register AttachmentRepository as scoped, using EF Core DbContext
|
||||
services.AddScoped<IAttachmentRepository, AttachmentRepository>();
|
||||
services.AddSingleton<IMailListener, MailListener>();
|
||||
services.AddSingleton<IXmlParser, XmlParser>();
|
||||
services.AddSingleton<IPdfParser, PdfParser>();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user