refactor: Simplify ArchiveAsync method signatures and update related implementations

This commit is contained in:
Giuliano Paschoalino 2025-06-25 16:09:56 -03:00
parent e6b2180c94
commit ad7dc587b2
13 changed files with 35 additions and 52 deletions

View File

@ -32,7 +32,7 @@ namespace ComplianceNFs.Core.Application
// Handles archiving of files // Handles archiving of files
public interface IArchivingService public interface IArchivingService
{ {
Task ArchiveAsync(EnergyInvoice invoice, byte[] rawFile); Task ArchiveAsync(EnergyInvoice invoice);
} }
// For streaming invoice status updates (for Monitor) // For streaming invoice status updates (for Monitor)

View File

@ -92,24 +92,15 @@ namespace ComplianceNFs.Core.Application.Services
} }
// Handles matching logic for invoices // Handles matching logic for invoices
public class MatchingService : IMatchingService public class MatchingService(IAccessDbRepository accessDbRepository, ILogger<MatchingService> logger) : IMatchingService
{ {
private readonly IAccessDbRepository _accessDbRepository;
private readonly ILogger<MatchingService> _logger;
public MatchingService(IAccessDbRepository accessDbRepository, ILogger<MatchingService> logger)
{
_accessDbRepository = accessDbRepository;
_logger = logger;
}
public Task MatchAsync(EnergyInvoice invoice) public Task MatchAsync(EnergyInvoice invoice)
{ {
try try
{ {
_logger.LogInformation("Matching invoice {InvoiceId}", invoice.InvoiceId); logger.LogInformation("Matching invoice {InvoiceId}", invoice.InvoiceId);
// Example: Primary match logic (simplified) // Example: Primary match logic (simplified)
var records = _accessDbRepository.GetByCnpj(invoice.CnpjComp ?? throw new ArgumentNullException("CnpjComp is required")); var records = accessDbRepository.GetByCnpj(invoice.CnpjComp ?? throw new ArgumentNullException(null, nameof(invoice.CnpjComp)));
if (records == null || records.ToList().Count == 0) if (records == null || records.ToList().Count == 0)
{ {
invoice.Status = InvoiceStatus.NotFound; invoice.Status = InvoiceStatus.NotFound;
@ -164,7 +155,7 @@ namespace ComplianceNFs.Core.Application.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Error matching invoice {InvoiceId}", invoice.InvoiceId); logger.LogError(ex, "Error matching invoice {InvoiceId}", invoice.InvoiceId);
throw; throw;
} }
return Task.CompletedTask; return Task.CompletedTask;
@ -211,9 +202,10 @@ namespace ComplianceNFs.Core.Application.Services
{ {
private readonly IFileArchiver _fileArchiver = fileArchiver; private readonly IFileArchiver _fileArchiver = fileArchiver;
public Task ArchiveAsync(EnergyInvoice invoice, byte[] rawFile) public Task ArchiveAsync(EnergyInvoice invoice)
{ {
return _fileArchiver.ArchiveAsync(invoice, rawFile); _fileArchiver.ArchiveAsync(invoice);
return Task.CompletedTask;
} }
} }
} }

View File

@ -38,6 +38,6 @@ namespace ComplianceNFs.Core.Ports
public interface IFileArchiver public interface IFileArchiver
{ {
Task ArchiveAsync(Entities.EnergyInvoice invoice, byte[] rawFile); void ArchiveAsync(Entities.EnergyInvoice invoice);
} }
} }

View File

@ -14,7 +14,7 @@ namespace ComplianceNFs.Infrastructure.Tests
{ {
// Arrange // Arrange
var expected = new List<BuyingRecord> { var expected = new List<BuyingRecord> {
new BuyingRecord { CodTE = 180310221018240701, CnpjComp = "06272575007403", CnpjVend = "13777004000122", MontLO = 24.72m, PrecLO = 147.29m } new() { CodTE = 180310221018240701, CnpjComp = "06272575007403", CnpjVend = "13777004000122", MontLO = 24.72m, PrecLO = 147.29m }
}; };
var mockRepo = new Mock<IAccessDbRepository>(); var mockRepo = new Mock<IAccessDbRepository>();
mockRepo.Setup(r => r.GetByCnpj("06272575007403")).Returns(expected); mockRepo.Setup(r => r.GetByCnpj("06272575007403")).Returns(expected);

View File

@ -29,10 +29,10 @@ namespace ComplianceNFs.Infrastructure.Tests
var fileBytes = new byte[] { 1, 2, 3 }; var fileBytes = new byte[] { 1, 2, 3 };
// Act // Act
await service.ArchiveAsync(invoice, fileBytes); await service.ArchiveAsync(invoice);
// Assert // Assert
mockArchiver.Verify(a => a.ArchiveAsync(invoice, fileBytes), Times.Once); mockArchiver.Verify(a => a.ArchiveAsync(invoice), Times.Once);
} }
} }
} }

View File

@ -46,9 +46,8 @@ namespace ComplianceNFs.Infrastructure.Tests
} }
// Expose protected method for test // Expose protected method for test
private class TestableMailListener : MailListener private class TestableMailListener(IConfiguration config, ILogger<MailListener> logger) : MailListener(config, logger)
{ {
public TestableMailListener(IConfiguration config, ILogger<MailListener> logger) : base(config, logger) { }
public new void RaiseNewMailReceivedForTest(MailMessage mail) => base.RaiseNewMailReceivedForTest(mail); public new void RaiseNewMailReceivedForTest(MailMessage mail) => base.RaiseNewMailReceivedForTest(mail);
} }
} }

View File

@ -25,7 +25,7 @@ namespace ComplianceNFs.Infrastructure.Tests
Filename = "file.xml", Filename = "file.xml",
Status = InvoiceStatus.Validated Status = InvoiceStatus.Validated
}; };
mockStream.Setup(s => s.GetRecent(It.IsAny<int>())).Returns(new[] { testInvoice }); mockStream.Setup(s => s.GetRecent(It.IsAny<int>())).Returns([testInvoice]);
var viewModel = new MonitorViewModel(mockStream.Object); var viewModel = new MonitorViewModel(mockStream.Object);
// Assert // Assert
@ -38,7 +38,7 @@ namespace ComplianceNFs.Infrastructure.Tests
{ {
// Arrange // Arrange
var mockStream = new Mock<IInvoiceStatusStream>(); var mockStream = new Mock<IInvoiceStatusStream>();
mockStream.Setup(s => s.GetRecent(It.IsAny<int>())).Returns(Array.Empty<EnergyInvoice>()); mockStream.Setup(s => s.GetRecent(It.IsAny<int>())).Returns([]);
var viewModel = new MonitorViewModel(mockStream.Object); var viewModel = new MonitorViewModel(mockStream.Object);
var newInvoice = new EnergyInvoice var newInvoice = new EnergyInvoice
{ {

View File

@ -55,8 +55,8 @@ namespace ComplianceNFs.Infrastructure.Tests
var mockLogger = new Mock<ILogger<MatchingService>>(); var mockLogger = new Mock<ILogger<MatchingService>>();
var invoice = new EnergyInvoice { CnpjComp = "123", CnpjVend = "456", MontNF = 300, PrecNF = 600, MailId = "m", ConversationId = "c", SupplierEmail = "s", ReceivedDate = DateTime.Now, InvoiceId = 1, Filename = "f.xml" }; var invoice = new EnergyInvoice { CnpjComp = "123", CnpjVend = "456", MontNF = 300, PrecNF = 600, MailId = "m", ConversationId = "c", SupplierEmail = "s", ReceivedDate = DateTime.Now, InvoiceId = 1, Filename = "f.xml" };
var records = new List<BuyingRecord> { var records = new List<BuyingRecord> {
new BuyingRecord { CnpjComp = "123", CnpjVend = "456", MontLO = 100, PrecLO = 200, CodTE = 1 }, new() { CnpjComp = "123", CnpjVend = "456", MontLO = 100, PrecLO = 200, CodTE = 1 },
new BuyingRecord { CnpjComp = "123", CnpjVend = "456", MontLO = 200, PrecLO = 400, CodTE = 2 } new() { CnpjComp = "123", CnpjVend = "456", MontLO = 200, PrecLO = 400, CodTE = 2 }
}; };
mockRepo.Setup(r => r.GetByCnpj("123")).Returns(records); mockRepo.Setup(r => r.GetByCnpj("123")).Returns(records);
var service = new MatchingService(mockRepo.Object, mockLogger.Object); var service = new MatchingService(mockRepo.Object, mockLogger.Object);
@ -87,8 +87,8 @@ namespace ComplianceNFs.Infrastructure.Tests
Filename = "f.xml" Filename = "f.xml"
}; };
var records = new List<BuyingRecord> { var records = new List<BuyingRecord> {
new BuyingRecord { CnpjComp = "123", CnpjVend = "456", MontLO = 100, PrecLO = 200, CodTE = 1 }, new() { CnpjComp = "123", CnpjVend = "456", MontLO = 100, PrecLO = 200, CodTE = 1 },
new BuyingRecord { CnpjComp = "123", CnpjVend = "456", MontLO = 200, PrecLO = 400, CodTE = 2 } new() { CnpjComp = "123", CnpjVend = "456", MontLO = 200, PrecLO = 400, CodTE = 2 }
}; };
mockRepo.Setup(r => r.GetByCnpj("123")).Returns(records); mockRepo.Setup(r => r.GetByCnpj("123")).Returns(records);
var service = new MatchingService(mockRepo.Object, mockLogger.Object); var service = new MatchingService(mockRepo.Object, mockLogger.Object);

View File

@ -34,7 +34,7 @@ namespace ComplianceNFs.Infrastructure.Tests
}; };
var data = new byte[] { 1, 2, 3, 4 }; var data = new byte[] { 1, 2, 3, 4 };
await archiver.ArchiveAsync(invoice, data); archiver.ArchiveAsync(invoice);
var expectedFolder = Path.Combine(_testBasePath, "Validated"); var expectedFolder = Path.Combine(_testBasePath, "Validated");
var expectedFile = Path.Combine(expectedFolder, "testfile.txt"); var expectedFile = Path.Combine(expectedFolder, "testfile.txt");
@ -59,11 +59,10 @@ namespace ComplianceNFs.Infrastructure.Tests
ReceivedDate = DateTime.Now, ReceivedDate = DateTime.Now,
InvoiceId = 1 InvoiceId = 1
}; };
var data1 = new byte[] { 1, 2, 3 };
var data2 = new byte[] { 9, 8, 7 }; var data2 = new byte[] { 9, 8, 7 };
await archiver.ArchiveAsync(invoice, data1); archiver.ArchiveAsync(invoice);
await archiver.ArchiveAsync(invoice, data2); archiver.ArchiveAsync(invoice);
var expectedFile = Path.Combine(_testBasePath, "Validated", "testfile.txt"); var expectedFile = Path.Combine(_testBasePath, "Validated", "testfile.txt");
var fileData = await File.ReadAllBytesAsync(expectedFile); var fileData = await File.ReadAllBytesAsync(expectedFile);
@ -74,6 +73,7 @@ namespace ComplianceNFs.Infrastructure.Tests
{ {
if (Directory.Exists(_testBasePath)) if (Directory.Exists(_testBasePath))
Directory.Delete(_testBasePath, true); Directory.Delete(_testBasePath, true);
GC.SuppressFinalize(this);
} }
} }

View File

@ -12,8 +12,9 @@ namespace ComplianceNFs.Infrastructure.Archiving
{ {
private readonly string _basePath = basePath; private readonly string _basePath = basePath;
public async Task ArchiveAsync(EnergyInvoice invoice, byte[] rawFile) public void ArchiveAsync(EnergyInvoice invoice)
{ {
var sourceFile = Path.Combine(_basePath, invoice.Filename);
// Create subfolder for invoice.Status // Create subfolder for invoice.Status
var statusFolder = Path.Combine(_basePath, invoice.Status.ToString()); var statusFolder = Path.Combine(_basePath, invoice.Status.ToString());
if (!Directory.Exists(statusFolder)) if (!Directory.Exists(statusFolder))
@ -23,7 +24,7 @@ namespace ComplianceNFs.Infrastructure.Archiving
// Build file path // Build file path
var filePath = Path.Combine(statusFolder, invoice.Filename); var filePath = Path.Combine(statusFolder, invoice.Filename);
// Write file (overwrite if exists) // Write file (overwrite if exists)
await File.WriteAllBytesAsync(filePath, rawFile); File.Move(sourceFile, filePath);
} }
} }
} }

View File

@ -10,22 +10,13 @@ using Microsoft.Extensions.Logging;
namespace ComplianceNFs.Infrastructure.Repositories namespace ComplianceNFs.Infrastructure.Repositories
{ {
// Placeholder: fill in actual SQL and mapping logic // Placeholder: fill in actual SQL and mapping logic
public class AttachmentRepository : IAttachmentRepository public class AttachmentRepository(string connectionString, ILogger<AttachmentRepository> logger) : IAttachmentRepository
{ {
private readonly string _connectionString;
private readonly ILogger<AttachmentRepository> _logger;
public AttachmentRepository(string connectionString, ILogger<AttachmentRepository> logger)
{
_connectionString = connectionString;
_logger = logger;
}
public async Task SaveRawAsync(EnergyInvoice invoice) public async Task SaveRawAsync(EnergyInvoice invoice)
{ {
try try
{ {
using var conn = new NpgsqlConnection(_connectionString); using var conn = new NpgsqlConnection(connectionString);
await conn.OpenAsync(); await conn.OpenAsync();
var cmd = conn.CreateCommand(); var cmd = conn.CreateCommand();
cmd.CommandText = @"INSERT INTO attachments ( cmd.CommandText = @"INSERT INTO attachments (
@ -55,11 +46,11 @@ namespace ComplianceNFs.Infrastructure.Repositories
cmd.Parameters.AddWithValue("@discrepancy", (object?)invoice.DiscrepancyNotes ?? DBNull.Value); cmd.Parameters.AddWithValue("@discrepancy", (object?)invoice.DiscrepancyNotes ?? DBNull.Value);
cmd.Parameters.AddWithValue("@metadata", Newtonsoft.Json.JsonConvert.SerializeObject(invoice)); cmd.Parameters.AddWithValue("@metadata", Newtonsoft.Json.JsonConvert.SerializeObject(invoice));
await cmd.ExecuteNonQueryAsync(); await cmd.ExecuteNonQueryAsync();
_logger.LogInformation("Saved raw invoice {InvoiceId} to attachments table.", invoice.InvoiceId); logger.LogInformation("Saved raw invoice {InvoiceId} to attachments table.", invoice.InvoiceId);
} }
catch (Exception ex) 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 attachments table.", invoice.InvoiceId);
throw; throw;
} }
} }
@ -68,7 +59,7 @@ namespace ComplianceNFs.Infrastructure.Repositories
{ {
try try
{ {
using var conn = new NpgsqlConnection(_connectionString); using var conn = new NpgsqlConnection(connectionString);
await conn.OpenAsync(); await conn.OpenAsync();
var cmd = conn.CreateCommand(); 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.CommandText = @"UPDATE attachments SET matched_cod_te = @matched_cod_te, status = @status, discrepancy = @discrepancy WHERE invoice_id = @invoice_id";
@ -77,11 +68,11 @@ namespace ComplianceNFs.Infrastructure.Repositories
cmd.Parameters.AddWithValue("@discrepancy", (object?)notes ?? DBNull.Value); cmd.Parameters.AddWithValue("@discrepancy", (object?)notes ?? DBNull.Value);
cmd.Parameters.AddWithValue("@invoice_id", invoiceId); cmd.Parameters.AddWithValue("@invoice_id", invoiceId);
await cmd.ExecuteNonQueryAsync(); await cmd.ExecuteNonQueryAsync();
_logger.LogInformation("Updated match for invoice {InvoiceId}.", invoiceId); logger.LogInformation("Updated match for invoice {InvoiceId}.", invoiceId);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Error updating match for invoice {InvoiceId}.", invoiceId); logger.LogError(ex, "Error updating match for invoice {InvoiceId}.", invoiceId);
throw; throw;
} }
} }

View File

@ -32,6 +32,6 @@ namespace ComplianceNFs.Monitor
public class DummyStatusStream : IInvoiceStatusStream public class DummyStatusStream : IInvoiceStatusStream
{ {
public event Action<Core.Entities.EnergyInvoice>? StatusUpdated { add { } remove { } } public event Action<Core.Entities.EnergyInvoice>? StatusUpdated { add { } remove { } }
public IEnumerable<Core.Entities.EnergyInvoice> GetRecent(int count = 100) => Array.Empty<Core.Entities.EnergyInvoice>(); public IEnumerable<Core.Entities.EnergyInvoice> GetRecent(int count = 100) => [];
} }
} }

View File

@ -44,7 +44,7 @@ public class Worker(ILogger<Worker> logger,
} }
// 4. Archive // 4. Archive
// (Assume raw file is available or can be loaded if needed) // (Assume raw file is available or can be loaded if needed)
// await _archivingService.ArchiveAsync(invoice, rawFile); await _archivingService.ArchiveAsync(invoice);
_logger.LogInformation("Invoice {NumeroNF} processed with status: {Status}", invoice.NumeroNF, invoice.Status); _logger.LogInformation("Invoice {NumeroNF} processed with status: {Status}", invoice.NumeroNF, invoice.Status);
} }
catch (Exception ex) catch (Exception ex)