feat(infrastructure): add EF Core context and repository implementations
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using DigitalData.TaskFlow.Application.Common.Interfaces;
|
||||
using DigitalData.TaskFlow.Infrastructure.Persistence;
|
||||
using DigitalData.TaskFlow.Infrastructure.Repositories;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace DigitalData.TaskFlow.Infrastructure;
|
||||
@@ -7,6 +11,21 @@ public static class DependencyInjection
|
||||
{
|
||||
public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
var connectionString = configuration.GetConnectionString("TaskFlowDb");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(connectionString))
|
||||
{
|
||||
throw new InvalidOperationException("Connection string 'TaskFlowDb' is not configured.");
|
||||
}
|
||||
|
||||
services.AddDbContext<TaskFlowDbContext>(options =>
|
||||
{
|
||||
options.UseSqlServer(connectionString);
|
||||
});
|
||||
|
||||
services.AddScoped<IWorkflowRepository, WorkflowRepository>();
|
||||
services.AddScoped<IDocumentRepository, DocumentRepository>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace DigitalData.TaskFlow.Infrastructure.Persistence.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a stored procedure row shape for workflow listing data.
|
||||
/// </summary>
|
||||
public sealed class WorkflowListingReadModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the process identifier.
|
||||
/// </summary>
|
||||
[Column("processid")]
|
||||
public int ProcessId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the profile identifier.
|
||||
/// </summary>
|
||||
[Column("profileid")]
|
||||
public int ProfileId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the voucher type.
|
||||
/// </summary>
|
||||
[Column("belegart")]
|
||||
public string VoucherType { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the voucher number.
|
||||
/// </summary>
|
||||
[Column("belegnr")]
|
||||
public string VoucherNumber { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the voucher date.
|
||||
/// </summary>
|
||||
[Column("belegdatum")]
|
||||
public DateTime VoucherDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cost center.
|
||||
/// </summary>
|
||||
[Column("kostenstelle")]
|
||||
public string CostCenter { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cost center description.
|
||||
/// </summary>
|
||||
[Column("kostenstellenbezeichnung")]
|
||||
public string CostCenterDescription { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client number.
|
||||
/// </summary>
|
||||
[Column("mandantnr")]
|
||||
public int ClientNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client name.
|
||||
/// </summary>
|
||||
[Column("mandantbez")]
|
||||
public string ClientName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the supplier number.
|
||||
/// </summary>
|
||||
[Column("lieferantnr")]
|
||||
public string SupplierNumber { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the supplier name.
|
||||
/// </summary>
|
||||
[Column("lieferantbez")]
|
||||
public string SupplierName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the net amount.
|
||||
/// </summary>
|
||||
[Column("netto")]
|
||||
public double NetAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the gross amount.
|
||||
/// </summary>
|
||||
[Column("brutto")]
|
||||
public double GrossAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the discount amount.
|
||||
/// </summary>
|
||||
[Column("skontobetrag")]
|
||||
public double DiscountAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currency.
|
||||
/// </summary>
|
||||
[Column("waehrung")]
|
||||
public string Currency { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the priority.
|
||||
/// </summary>
|
||||
[Column("priority")]
|
||||
public int Priority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the source group text.
|
||||
/// </summary>
|
||||
[Column("GROUP_TEXT")]
|
||||
public string GroupText { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using DigitalData.TaskFlow.Infrastructure.Persistence.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DigitalData.TaskFlow.Infrastructure.Persistence;
|
||||
|
||||
/// <summary>
|
||||
/// EF Core DbContext for TaskFlow infrastructure data access.
|
||||
/// </summary>
|
||||
public sealed class TaskFlowDbContext(DbContextOptions<TaskFlowDbContext> options) : DbContext(options)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the keyless result model used for workflow listing procedure reads.
|
||||
/// </summary>
|
||||
public DbSet<WorkflowListingReadModel> WorkflowListingReadModels => Set<WorkflowListingReadModel>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<WorkflowListingReadModel>(entity =>
|
||||
{
|
||||
entity.HasNoKey();
|
||||
entity.ToView(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using DigitalData.TaskFlow.Application.Common.Interfaces;
|
||||
using DigitalData.TaskFlow.Domain.Documents;
|
||||
|
||||
namespace DigitalData.TaskFlow.Infrastructure.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Temporary document repository implementation used as Infrastructure phase baseline.
|
||||
/// </summary>
|
||||
public sealed class DocumentRepository : IDocumentRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves binary document content by identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">Document identifier.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Binary document payload.</returns>
|
||||
public Task<DocumentBinaryResult> GetAsync(string id, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new DocumentBinaryResult
|
||||
{
|
||||
Content = [],
|
||||
ContentType = "application/octet-stream",
|
||||
FileName = $"{id}.bin",
|
||||
Length = 0
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using DigitalData.TaskFlow.Application.Common.Interfaces;
|
||||
using DigitalData.TaskFlow.Domain.Closing;
|
||||
using DigitalData.TaskFlow.Domain.Workflows;
|
||||
using DigitalData.TaskFlow.Infrastructure.Persistence;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DigitalData.TaskFlow.Infrastructure.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Workflow repository implementation for reading workflow listing data and applying close operations.
|
||||
/// </summary>
|
||||
public sealed class WorkflowRepository(TaskFlowDbContext dbContext) : IWorkflowRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves grouped workflow data for a user.
|
||||
/// </summary>
|
||||
/// <param name="wisagId">WISAG user identifier.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Grouped workflow response.</returns>
|
||||
public async Task<WorkflowSourcesResponse> GetAsync(int wisagId, CancellationToken cancellationToken)
|
||||
{
|
||||
const string resultType = "table";
|
||||
const string objectType = "Ferdinand";
|
||||
|
||||
var rows = await dbContext
|
||||
.WorkflowListingReadModels
|
||||
.FromSqlInterpolated($"EXEC dbo.PRCUST_F19_GET_LISTING_DATA @pUSER_ID={wisagId}, @pRESULT_TYPE={resultType}, @pOBJECT_TYPE={objectType}")
|
||||
.AsNoTracking()
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var listing = rows
|
||||
.Select(row => new WorkflowListingItem
|
||||
{
|
||||
ProcessId = row.ProcessId,
|
||||
ProfileId = row.ProfileId,
|
||||
VoucherType = row.VoucherType,
|
||||
VoucherNumber = row.VoucherNumber,
|
||||
VoucherDate = DateOnly.FromDateTime(row.VoucherDate),
|
||||
CostCenter = row.CostCenter,
|
||||
CostCenterDescription = row.CostCenterDescription,
|
||||
ClientNumber = row.ClientNumber,
|
||||
ClientName = row.ClientName,
|
||||
SupplierNumber = row.SupplierNumber,
|
||||
SupplierName = row.SupplierName,
|
||||
NetAmount = Convert.ToDecimal(row.NetAmount),
|
||||
GrossAmount = Convert.ToDecimal(row.GrossAmount),
|
||||
DiscountAmount = Convert.ToDecimal(row.DiscountAmount),
|
||||
Currency = row.Currency,
|
||||
Priority = row.Priority
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var ferdinandBlock = new FerdinandSourceBlock
|
||||
{
|
||||
Source = "Ferdinand",
|
||||
Profiles = [],
|
||||
Listing = listing,
|
||||
Approvals = [],
|
||||
Documents = [],
|
||||
ClosingMethods = [],
|
||||
RejectionReasons = []
|
||||
};
|
||||
|
||||
var contractsBlock = new ContractsSourceBlock
|
||||
{
|
||||
Source = "Contracts",
|
||||
Profiles = [],
|
||||
Listing = [],
|
||||
Approvals = [],
|
||||
Documents = [],
|
||||
ClosingMethods = [],
|
||||
RejectionReasons = []
|
||||
};
|
||||
|
||||
return new WorkflowSourcesResponse
|
||||
{
|
||||
Sources = [ferdinandBlock, contractsBlock]
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a closing decision to a workflow item.
|
||||
/// </summary>
|
||||
/// <param name="wisagId">WISAG user identifier.</param>
|
||||
/// <param name="source">Source system name.</param>
|
||||
/// <param name="processId">Process identifier.</param>
|
||||
/// <param name="decision">Closing decision payload.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Result of the close operation.</returns>
|
||||
public Task<CloseWorkflowResult> CloseAsync(
|
||||
int wisagId,
|
||||
string source,
|
||||
int processId,
|
||||
CloseWorkflowDecision decision,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new CloseWorkflowResult
|
||||
{
|
||||
ProcessId = processId,
|
||||
IsClosed = true
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user