Regelsteuerung grafisch und per C# Templates
This commit is contained in:
@@ -3,18 +3,24 @@
|
|||||||
@using System.Reflection
|
@using System.Reflection
|
||||||
@using TrafagSalesExporter.Data
|
@using TrafagSalesExporter.Data
|
||||||
@using TrafagSalesExporter.Models
|
@using TrafagSalesExporter.Models
|
||||||
|
@using TrafagSalesExporter.Services
|
||||||
@inject IDbContextFactory<AppDbContext> DbFactory
|
@inject IDbContextFactory<AppDbContext> DbFactory
|
||||||
|
@inject ITransformationCatalog TransformationCatalog
|
||||||
@inject ISnackbar Snackbar
|
@inject ISnackbar Snackbar
|
||||||
|
|
||||||
<PageTitle>Transformationen</PageTitle>
|
<PageTitle>Transformationen</PageTitle>
|
||||||
|
|
||||||
<MudText Typo="Typo.h4" Class="mb-4">Transformer Ansicht</MudText>
|
<MudText Typo="Typo.h4" Class="mb-4">Transformer Ansicht</MudText>
|
||||||
<MudText Typo="Typo.body1" Class="mb-4">Definiere pro Quellsystem (SAP, BI1, SAGE) Feld-Remapping und Transformationen.</MudText>
|
<MudText Typo="Typo.body1" Class="mb-4">Definiere pro Quellsystem einfache Feldregeln und komplexe record-basierte Strategien.</MudText>
|
||||||
|
|
||||||
<MudPaper Class="pa-4" Elevation="1">
|
<MudPaper Class="pa-4" Elevation="1">
|
||||||
|
<MudAlert Severity="Severity.Info" Dense="true" Variant="Variant.Outlined" Class="mb-3">
|
||||||
|
`Value`-Regeln arbeiten feldweise. `Record`-Regeln rufen eine registrierte C#-Strategie auf und koennen mehrere Felder eines Datensatzes verwenden.
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
<MudStack Row="true" Spacing="2" Class="mb-3">
|
<MudStack Row="true" Spacing="2" Class="mb-3">
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" OnClick="AddRule">
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" OnClick="AddRule">
|
||||||
Regel hinzufügen
|
Regel hinzufuegen
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Outlined" Color="Color.Secondary" StartIcon="@Icons.Material.Filled.Save" OnClick="SaveAllAsync">
|
<MudButton Variant="Variant.Outlined" Color="Color.Secondary" StartIcon="@Icons.Material.Filled.Save" OnClick="SaveAllAsync">
|
||||||
Alle speichern
|
Alle speichern
|
||||||
@@ -25,6 +31,7 @@
|
|||||||
<HeaderContent>
|
<HeaderContent>
|
||||||
<MudTh>Aktiv</MudTh>
|
<MudTh>Aktiv</MudTh>
|
||||||
<MudTh>System</MudTh>
|
<MudTh>System</MudTh>
|
||||||
|
<MudTh>Scope</MudTh>
|
||||||
<MudTh>Source</MudTh>
|
<MudTh>Source</MudTh>
|
||||||
<MudTh>Target</MudTh>
|
<MudTh>Target</MudTh>
|
||||||
<MudTh>Typ</MudTh>
|
<MudTh>Typ</MudTh>
|
||||||
@@ -38,15 +45,23 @@
|
|||||||
<MudSelect T="string" Value="@context.SourceSystem" ValueChanged="@(v => context.SourceSystem = v)" Dense>
|
<MudSelect T="string" Value="@context.SourceSystem" ValueChanged="@(v => context.SourceSystem = v)" Dense>
|
||||||
@foreach (var system in _systems)
|
@foreach (var system in _systems)
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="system">@system</MudSelectItem>
|
<MudSelectItem Value="@system">@system</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd>
|
<MudTd>
|
||||||
<MudSelect T="string" Value="@context.SourceField" ValueChanged="@(v => context.SourceField = v)" Dense>
|
<MudSelect T="string" Value="@context.RuleScope" ValueChanged="@(v => ChangeRuleScope(context, v))" Dense>
|
||||||
|
@foreach (var scope in _ruleScopes)
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@scope">@scope</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
<MudSelect T="string" Value="@context.SourceField" ValueChanged="@(v => context.SourceField = v)" Dense Disabled="@IsRecordScope(context)">
|
||||||
@foreach (var field in _recordFields)
|
@foreach (var field in _recordFields)
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="field">@field</MudSelectItem>
|
<MudSelectItem Value="@field">@field</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
@@ -54,21 +69,21 @@
|
|||||||
<MudSelect T="string" Value="@context.TargetField" ValueChanged="@(v => context.TargetField = v)" Dense>
|
<MudSelect T="string" Value="@context.TargetField" ValueChanged="@(v => context.TargetField = v)" Dense>
|
||||||
@foreach (var field in _recordFields)
|
@foreach (var field in _recordFields)
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="field">@field</MudSelectItem>
|
<MudSelectItem Value="@field">@field</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd>
|
<MudTd>
|
||||||
<MudSelect T="string" Value="@context.TransformationType" ValueChanged="@(v => context.TransformationType = v)" Dense>
|
<MudSelect T="string" Value="@context.TransformationType" ValueChanged="@(v => context.TransformationType = v)" Dense>
|
||||||
@foreach (var type in _types)
|
@foreach (var type in GetTypesForScope(context.RuleScope))
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="type">@type</MudSelectItem>
|
<MudSelectItem Value="@type.Key">@type.Key</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd>
|
<MudTd>
|
||||||
<MudTextField T="string" Value="@context.Argument" ValueChanged="@(v => context.Argument = v)"
|
<MudTextField T="string" Value="@context.Argument" ValueChanged="@(v => context.Argument = v)"
|
||||||
HelperText="Replace: alt=>neu" />
|
HelperText="@GetArgumentHelperText(context)" />
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd>
|
<MudTd>
|
||||||
<MudNumericField T="int" Value="@context.SortOrder" ValueChanged="@(v => context.SortOrder = v)" Dense />
|
<MudNumericField T="int" Value="@context.SortOrder" ValueChanged="@(v => context.SortOrder = v)" Dense />
|
||||||
@@ -82,8 +97,8 @@
|
|||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private readonly string[] _systems = ["SAP", "BI1", "SAGE"];
|
private readonly string[] _systems = ["SAP", "BI1", "SAGE", "MANUAL_EXCEL"];
|
||||||
private readonly string[] _types = ["Copy", "Uppercase", "Lowercase", "Prefix", "Suffix", "Replace", "Constant"];
|
private readonly string[] _ruleScopes = ["Value", "Record"];
|
||||||
private readonly string[] _recordFields = typeof(SalesRecord)
|
private readonly string[] _recordFields = typeof(SalesRecord)
|
||||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||||
.Select(p => p.Name)
|
.Select(p => p.Name)
|
||||||
@@ -91,9 +106,11 @@
|
|||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
private List<FieldTransformationRule> _rules = new();
|
private List<FieldTransformationRule> _rules = new();
|
||||||
|
private IReadOnlyList<TransformationCatalogItem> _catalogItems = [];
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
_catalogItems = TransformationCatalog.GetAll();
|
||||||
await LoadAsync();
|
await LoadAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +118,15 @@
|
|||||||
{
|
{
|
||||||
using var db = await DbFactory.CreateDbContextAsync();
|
using var db = await DbFactory.CreateDbContextAsync();
|
||||||
_rules = await db.FieldTransformationRules.OrderBy(r => r.SortOrder).ThenBy(r => r.Id).ToListAsync();
|
_rules = await db.FieldTransformationRules.OrderBy(r => r.SortOrder).ThenBy(r => r.Id).ToListAsync();
|
||||||
|
|
||||||
|
foreach (var rule in _rules)
|
||||||
|
{
|
||||||
|
rule.RuleScope = string.IsNullOrWhiteSpace(rule.RuleScope) ? "Value" : rule.RuleScope;
|
||||||
|
if (!GetTypesForScope(rule.RuleScope).Any(x => string.Equals(x.Key, rule.TransformationType, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
rule.TransformationType = GetTypesForScope(rule.RuleScope).FirstOrDefault()?.Key ?? "Copy";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRule()
|
private void AddRule()
|
||||||
@@ -109,6 +135,7 @@
|
|||||||
_rules.Add(new FieldTransformationRule
|
_rules.Add(new FieldTransformationRule
|
||||||
{
|
{
|
||||||
SourceSystem = "SAP",
|
SourceSystem = "SAP",
|
||||||
|
RuleScope = "Value",
|
||||||
SourceField = nameof(SalesRecord.Material),
|
SourceField = nameof(SalesRecord.Material),
|
||||||
TargetField = nameof(SalesRecord.Material),
|
TargetField = nameof(SalesRecord.Material),
|
||||||
TransformationType = "Copy",
|
TransformationType = "Copy",
|
||||||
@@ -134,4 +161,35 @@
|
|||||||
Snackbar.Add("Transformationsregeln gespeichert.", Severity.Success);
|
Snackbar.Add("Transformationsregeln gespeichert.", Severity.Success);
|
||||||
await LoadAsync();
|
await LoadAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<TransformationCatalogItem> GetTypesForScope(string? ruleScope)
|
||||||
|
{
|
||||||
|
var scope = string.IsNullOrWhiteSpace(ruleScope) ? "Value" : ruleScope;
|
||||||
|
return TransformationCatalog.GetByScope(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsRecordScope(FieldTransformationRule rule)
|
||||||
|
=> string.Equals(rule.RuleScope, "Record", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
private void ChangeRuleScope(FieldTransformationRule rule, string scope)
|
||||||
|
{
|
||||||
|
rule.RuleScope = scope;
|
||||||
|
var firstType = GetTypesForScope(scope).FirstOrDefault()?.Key;
|
||||||
|
if (!string.IsNullOrWhiteSpace(firstType))
|
||||||
|
rule.TransformationType = firstType;
|
||||||
|
|
||||||
|
if (IsRecordScope(rule))
|
||||||
|
rule.SourceField = string.Empty;
|
||||||
|
else if (string.IsNullOrWhiteSpace(rule.SourceField))
|
||||||
|
rule.SourceField = nameof(SalesRecord.Material);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetArgumentHelperText(FieldTransformationRule rule)
|
||||||
|
{
|
||||||
|
var item = _catalogItems.FirstOrDefault(x =>
|
||||||
|
string.Equals(x.RuleScope, rule.RuleScope, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
string.Equals(x.Key, rule.TransformationType, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
return item?.Description ?? "Optionales Argument.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ public class FieldTransformationRule
|
|||||||
[Required]
|
[Required]
|
||||||
public string TransformationType { get; set; } = "Copy";
|
public string TransformationType { get; set; } = "Copy";
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string RuleScope { get; set; } = "Value";
|
||||||
|
|
||||||
public string Argument { get; set; } = string.Empty;
|
public string Argument { get; set; } = string.Empty;
|
||||||
|
|
||||||
public int SortOrder { get; set; }
|
public int SortOrder { get; set; }
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ builder.Services.AddSingleton<ITransformationStrategy, PrefixTransformationStrat
|
|||||||
builder.Services.AddSingleton<ITransformationStrategy, SuffixTransformationStrategy>();
|
builder.Services.AddSingleton<ITransformationStrategy, SuffixTransformationStrategy>();
|
||||||
builder.Services.AddSingleton<ITransformationStrategy, ReplaceTransformationStrategy>();
|
builder.Services.AddSingleton<ITransformationStrategy, ReplaceTransformationStrategy>();
|
||||||
builder.Services.AddSingleton<ITransformationStrategy, ConstantTransformationStrategy>();
|
builder.Services.AddSingleton<ITransformationStrategy, ConstantTransformationStrategy>();
|
||||||
|
builder.Services.AddSingleton<IRecordTransformationStrategy, FirstNonEmptyRecordTransformationStrategy>();
|
||||||
|
builder.Services.AddSingleton<ITransformationCatalog, TransformationCatalog>();
|
||||||
builder.Services.AddSingleton<IRecordTransformationService, RecordTransformationService>();
|
builder.Services.AddSingleton<IRecordTransformationService, RecordTransformationService>();
|
||||||
builder.Services.AddSingleton<IAppEventLogService, AppEventLogService>();
|
builder.Services.AddSingleton<IAppEventLogService, AppEventLogService>();
|
||||||
builder.Services.AddSingleton<IManagementCockpitService, ManagementCockpitService>();
|
builder.Services.AddSingleton<IManagementCockpitService, ManagementCockpitService>();
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ public class ConfigTransferService : IConfigTransferService
|
|||||||
SourceField = r.SourceField,
|
SourceField = r.SourceField,
|
||||||
TargetField = r.TargetField,
|
TargetField = r.TargetField,
|
||||||
TransformationType = r.TransformationType,
|
TransformationType = r.TransformationType,
|
||||||
|
RuleScope = r.RuleScope,
|
||||||
Argument = r.Argument,
|
Argument = r.Argument,
|
||||||
SortOrder = r.SortOrder,
|
SortOrder = r.SortOrder,
|
||||||
IsActive = r.IsActive
|
IsActive = r.IsActive
|
||||||
@@ -265,6 +266,7 @@ public class ConfigTransferService : IConfigTransferService
|
|||||||
SourceField = r.SourceField,
|
SourceField = r.SourceField,
|
||||||
TargetField = r.TargetField,
|
TargetField = r.TargetField,
|
||||||
TransformationType = r.TransformationType,
|
TransformationType = r.TransformationType,
|
||||||
|
RuleScope = r.RuleScope,
|
||||||
Argument = r.Argument,
|
Argument = r.Argument,
|
||||||
SortOrder = r.SortOrder,
|
SortOrder = r.SortOrder,
|
||||||
IsActive = r.IsActive
|
IsActive = r.IsActive
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ public class DatabaseInitializationService : IDatabaseInitializationService
|
|||||||
AddColumnIfMissing(db, "ExportSettings", "LocalConsolidatedExportFolder", "TEXT NOT NULL DEFAULT ''");
|
AddColumnIfMissing(db, "ExportSettings", "LocalConsolidatedExportFolder", "TEXT NOT NULL DEFAULT ''");
|
||||||
AddColumnIfMissing(db, "ExportLogs", "FilePath", "TEXT NOT NULL DEFAULT ''");
|
AddColumnIfMissing(db, "ExportLogs", "FilePath", "TEXT NOT NULL DEFAULT ''");
|
||||||
EnsureTransformationTable(db);
|
EnsureTransformationTable(db);
|
||||||
|
AddColumnIfMissing(db, "FieldTransformationRules", "RuleScope", "TEXT NOT NULL DEFAULT 'Value'");
|
||||||
EnsureSapSourceTable(db);
|
EnsureSapSourceTable(db);
|
||||||
EnsureSapJoinTable(db);
|
EnsureSapJoinTable(db);
|
||||||
EnsureSapFieldMappingTable(db);
|
EnsureSapFieldMappingTable(db);
|
||||||
@@ -440,6 +441,7 @@ CREATE TABLE IF NOT EXISTS FieldTransformationRules (
|
|||||||
SourceField TEXT NOT NULL,
|
SourceField TEXT NOT NULL,
|
||||||
TargetField TEXT NOT NULL,
|
TargetField TEXT NOT NULL,
|
||||||
TransformationType TEXT NOT NULL,
|
TransformationType TEXT NOT NULL,
|
||||||
|
RuleScope TEXT NOT NULL DEFAULT 'Value',
|
||||||
Argument TEXT NOT NULL DEFAULT '',
|
Argument TEXT NOT NULL DEFAULT '',
|
||||||
SortOrder INTEGER NOT NULL DEFAULT 0,
|
SortOrder INTEGER NOT NULL DEFAULT 0,
|
||||||
IsActive INTEGER NOT NULL DEFAULT 1
|
IsActive INTEGER NOT NULL DEFAULT 1
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public interface IRecordTransformationStrategy
|
||||||
|
{
|
||||||
|
string TransformationType { get; }
|
||||||
|
string Description => string.Empty;
|
||||||
|
void Transform(SalesRecord record, FieldTransformationRule rule);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public interface ITransformationCatalog
|
||||||
|
{
|
||||||
|
IReadOnlyList<TransformationCatalogItem> GetAll();
|
||||||
|
IReadOnlyList<TransformationCatalogItem> GetByScope(string ruleScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TransformationCatalogItem
|
||||||
|
{
|
||||||
|
public string Key { get; init; } = string.Empty;
|
||||||
|
public string RuleScope { get; init; } = string.Empty;
|
||||||
|
public string Description { get; init; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -3,5 +3,6 @@ namespace TrafagSalesExporter.Services;
|
|||||||
public interface ITransformationStrategy
|
public interface ITransformationStrategy
|
||||||
{
|
{
|
||||||
string TransformationType { get; }
|
string TransformationType { get; }
|
||||||
|
string Description => string.Empty;
|
||||||
object? Transform(object? sourceValue, string? argument);
|
object? Transform(object? sourceValue, string? argument);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,17 @@ namespace TrafagSalesExporter.Services;
|
|||||||
|
|
||||||
public class RecordTransformationService : IRecordTransformationService
|
public class RecordTransformationService : IRecordTransformationService
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, PropertyInfo> PropertyMap = typeof(SalesRecord)
|
internal static readonly Dictionary<string, PropertyInfo> PropertyMap = typeof(SalesRecord)
|
||||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||||
.ToDictionary(p => p.Name, p => p, StringComparer.OrdinalIgnoreCase);
|
.ToDictionary(p => p.Name, p => p, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
private readonly IReadOnlyDictionary<string, ITransformationStrategy> _strategies;
|
private readonly IReadOnlyDictionary<string, ITransformationStrategy> _strategies;
|
||||||
|
private readonly IReadOnlyDictionary<string, IRecordTransformationStrategy> _recordStrategies;
|
||||||
|
|
||||||
public RecordTransformationService(IEnumerable<ITransformationStrategy> strategies)
|
public RecordTransformationService(IEnumerable<ITransformationStrategy> strategies, IEnumerable<IRecordTransformationStrategy> recordStrategies)
|
||||||
{
|
{
|
||||||
_strategies = strategies.ToDictionary(s => s.TransformationType, StringComparer.OrdinalIgnoreCase);
|
_strategies = strategies.ToDictionary(s => s.TransformationType, StringComparer.OrdinalIgnoreCase);
|
||||||
|
_recordStrategies = recordStrategies.ToDictionary(s => s.TransformationType, StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Apply(List<SalesRecord> records, IEnumerable<FieldTransformationRule> rules)
|
public void Apply(List<SalesRecord> records, IEnumerable<FieldTransformationRule> rules)
|
||||||
@@ -32,6 +34,13 @@ public class RecordTransformationService : IRecordTransformationService
|
|||||||
|
|
||||||
private void ApplyRule(SalesRecord record, FieldTransformationRule rule)
|
private void ApplyRule(SalesRecord record, FieldTransformationRule rule)
|
||||||
{
|
{
|
||||||
|
if (string.Equals(rule.RuleScope, "Record", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (_recordStrategies.TryGetValue(rule.TransformationType, out var recordStrategy))
|
||||||
|
recordStrategy.Transform(record, rule);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!PropertyMap.TryGetValue(rule.SourceField, out var sourceProp)) return;
|
if (!PropertyMap.TryGetValue(rule.SourceField, out var sourceProp)) return;
|
||||||
if (!PropertyMap.TryGetValue(rule.TargetField, out var targetProp)) return;
|
if (!PropertyMap.TryGetValue(rule.TargetField, out var targetProp)) return;
|
||||||
|
|
||||||
@@ -43,7 +52,7 @@ public class RecordTransformationService : IRecordTransformationService
|
|||||||
SetPropertyValue(record, targetProp, result);
|
SetPropertyValue(record, targetProp, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetPropertyValue(SalesRecord record, PropertyInfo property, object? value)
|
internal static void SetPropertyValue(SalesRecord record, PropertyInfo property, object? value)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public class TransformationCatalog : ITransformationCatalog
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<TransformationCatalogItem> _items;
|
||||||
|
|
||||||
|
public TransformationCatalog(IEnumerable<ITransformationStrategy> valueStrategies, IEnumerable<IRecordTransformationStrategy> recordStrategies)
|
||||||
|
{
|
||||||
|
_items = valueStrategies
|
||||||
|
.Select(x => new TransformationCatalogItem
|
||||||
|
{
|
||||||
|
Key = x.TransformationType,
|
||||||
|
RuleScope = "Value",
|
||||||
|
Description = x.Description
|
||||||
|
})
|
||||||
|
.Concat(recordStrategies.Select(x => new TransformationCatalogItem
|
||||||
|
{
|
||||||
|
Key = x.TransformationType,
|
||||||
|
RuleScope = "Record",
|
||||||
|
Description = x.Description
|
||||||
|
}))
|
||||||
|
.OrderBy(x => x.RuleScope, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ThenBy(x => x.Key, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<TransformationCatalogItem> GetAll() => _items;
|
||||||
|
|
||||||
|
public IReadOnlyList<TransformationCatalogItem> GetByScope(string ruleScope)
|
||||||
|
=> _items
|
||||||
|
.Where(x => string.Equals(x.RuleScope, ruleScope, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
@@ -1,38 +1,46 @@
|
|||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
namespace TrafagSalesExporter.Services;
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
public sealed class CopyTransformationStrategy : ITransformationStrategy
|
public sealed class CopyTransformationStrategy : ITransformationStrategy
|
||||||
{
|
{
|
||||||
public string TransformationType => "Copy";
|
public string TransformationType => "Copy";
|
||||||
|
public string Description => "Kopiert Source nach Target.";
|
||||||
public object? Transform(object? sourceValue, string? argument) => sourceValue;
|
public object? Transform(object? sourceValue, string? argument) => sourceValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class UppercaseTransformationStrategy : ITransformationStrategy
|
public sealed class UppercaseTransformationStrategy : ITransformationStrategy
|
||||||
{
|
{
|
||||||
public string TransformationType => "Uppercase";
|
public string TransformationType => "Uppercase";
|
||||||
|
public string Description => "Wandelt Text in Grossbuchstaben.";
|
||||||
public object? Transform(object? sourceValue, string? argument) => sourceValue?.ToString()?.ToUpperInvariant();
|
public object? Transform(object? sourceValue, string? argument) => sourceValue?.ToString()?.ToUpperInvariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class LowercaseTransformationStrategy : ITransformationStrategy
|
public sealed class LowercaseTransformationStrategy : ITransformationStrategy
|
||||||
{
|
{
|
||||||
public string TransformationType => "Lowercase";
|
public string TransformationType => "Lowercase";
|
||||||
|
public string Description => "Wandelt Text in Kleinbuchstaben.";
|
||||||
public object? Transform(object? sourceValue, string? argument) => sourceValue?.ToString()?.ToLowerInvariant();
|
public object? Transform(object? sourceValue, string? argument) => sourceValue?.ToString()?.ToLowerInvariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class PrefixTransformationStrategy : ITransformationStrategy
|
public sealed class PrefixTransformationStrategy : ITransformationStrategy
|
||||||
{
|
{
|
||||||
public string TransformationType => "Prefix";
|
public string TransformationType => "Prefix";
|
||||||
|
public string Description => "Stellt Argument vor den Source-Wert.";
|
||||||
public object? Transform(object? sourceValue, string? argument) => $"{argument}{sourceValue}";
|
public object? Transform(object? sourceValue, string? argument) => $"{argument}{sourceValue}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class SuffixTransformationStrategy : ITransformationStrategy
|
public sealed class SuffixTransformationStrategy : ITransformationStrategy
|
||||||
{
|
{
|
||||||
public string TransformationType => "Suffix";
|
public string TransformationType => "Suffix";
|
||||||
|
public string Description => "Haengt Argument an den Source-Wert.";
|
||||||
public object? Transform(object? sourceValue, string? argument) => $"{sourceValue}{argument}";
|
public object? Transform(object? sourceValue, string? argument) => $"{sourceValue}{argument}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ReplaceTransformationStrategy : ITransformationStrategy
|
public sealed class ReplaceTransformationStrategy : ITransformationStrategy
|
||||||
{
|
{
|
||||||
public string TransformationType => "Replace";
|
public string TransformationType => "Replace";
|
||||||
|
public string Description => "Ersetzt in Text mit Syntax alt=>neu.";
|
||||||
|
|
||||||
public object? Transform(object? sourceValue, string? argument)
|
public object? Transform(object? sourceValue, string? argument)
|
||||||
{
|
{
|
||||||
@@ -54,5 +62,54 @@ public sealed class ReplaceTransformationStrategy : ITransformationStrategy
|
|||||||
public sealed class ConstantTransformationStrategy : ITransformationStrategy
|
public sealed class ConstantTransformationStrategy : ITransformationStrategy
|
||||||
{
|
{
|
||||||
public string TransformationType => "Constant";
|
public string TransformationType => "Constant";
|
||||||
|
public string Description => "Setzt das Target auf einen konstanten Wert aus Argument.";
|
||||||
public object? Transform(object? sourceValue, string? argument) => argument;
|
public object? Transform(object? sourceValue, string? argument) => argument;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class FirstNonEmptyRecordTransformationStrategy : IRecordTransformationStrategy
|
||||||
|
{
|
||||||
|
public string TransformationType => "FirstNonEmpty";
|
||||||
|
public string Description => "Record-Strategie: setzt Target aus dem ersten nicht-leeren Feld aus Argument, z.B. CustomerName|SupplierName|Name.";
|
||||||
|
|
||||||
|
public void Transform(SalesRecord record, FieldTransformationRule rule)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(rule.TargetField) || string.IsNullOrWhiteSpace(rule.Argument))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var propertyMap = RecordTransformationService.PropertyMap;
|
||||||
|
if (!propertyMap.TryGetValue(rule.TargetField, out var targetProperty))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sourceFields = rule.Argument
|
||||||
|
.Split(['|', ',', ';'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||||
|
|
||||||
|
foreach (var sourceField in sourceFields)
|
||||||
|
{
|
||||||
|
if (!propertyMap.TryGetValue(sourceField, out var sourceProperty))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var value = sourceProperty.GetValue(record);
|
||||||
|
if (IsMeaningfulValue(value))
|
||||||
|
{
|
||||||
|
RecordTransformationService.SetPropertyValue(record, targetProperty, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsMeaningfulValue(object? value)
|
||||||
|
{
|
||||||
|
if (value is null)
|
||||||
|
return false;
|
||||||
|
if (value is string text)
|
||||||
|
return !string.IsNullOrWhiteSpace(text);
|
||||||
|
if (value is DateTime date)
|
||||||
|
return date != default;
|
||||||
|
if (value is decimal decimalNumber)
|
||||||
|
return decimalNumber != 0m;
|
||||||
|
if (value is int intNumber)
|
||||||
|
return intNumber != 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user