Regelsteuerung grafisch und per C# Templates
This commit is contained in:
@@ -95,6 +95,7 @@ public class ConfigTransferService : IConfigTransferService
|
||||
SourceField = r.SourceField,
|
||||
TargetField = r.TargetField,
|
||||
TransformationType = r.TransformationType,
|
||||
RuleScope = r.RuleScope,
|
||||
Argument = r.Argument,
|
||||
SortOrder = r.SortOrder,
|
||||
IsActive = r.IsActive
|
||||
@@ -265,6 +266,7 @@ public class ConfigTransferService : IConfigTransferService
|
||||
SourceField = r.SourceField,
|
||||
TargetField = r.TargetField,
|
||||
TransformationType = r.TransformationType,
|
||||
RuleScope = r.RuleScope,
|
||||
Argument = r.Argument,
|
||||
SortOrder = r.SortOrder,
|
||||
IsActive = r.IsActive
|
||||
|
||||
@@ -71,6 +71,7 @@ public class DatabaseInitializationService : IDatabaseInitializationService
|
||||
AddColumnIfMissing(db, "ExportSettings", "LocalConsolidatedExportFolder", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "ExportLogs", "FilePath", "TEXT NOT NULL DEFAULT ''");
|
||||
EnsureTransformationTable(db);
|
||||
AddColumnIfMissing(db, "FieldTransformationRules", "RuleScope", "TEXT NOT NULL DEFAULT 'Value'");
|
||||
EnsureSapSourceTable(db);
|
||||
EnsureSapJoinTable(db);
|
||||
EnsureSapFieldMappingTable(db);
|
||||
@@ -440,6 +441,7 @@ CREATE TABLE IF NOT EXISTS FieldTransformationRules (
|
||||
SourceField TEXT NOT NULL,
|
||||
TargetField TEXT NOT NULL,
|
||||
TransformationType TEXT NOT NULL,
|
||||
RuleScope TEXT NOT NULL DEFAULT 'Value',
|
||||
Argument TEXT NOT NULL DEFAULT '',
|
||||
SortOrder INTEGER NOT NULL DEFAULT 0,
|
||||
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
|
||||
{
|
||||
string TransformationType { get; }
|
||||
string Description => string.Empty;
|
||||
object? Transform(object? sourceValue, string? argument);
|
||||
}
|
||||
|
||||
@@ -5,15 +5,17 @@ namespace TrafagSalesExporter.Services;
|
||||
|
||||
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)
|
||||
.ToDictionary(p => p.Name, p => p, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
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);
|
||||
_recordStrategies = recordStrategies.ToDictionary(s => s.TransformationType, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public void Apply(List<SalesRecord> records, IEnumerable<FieldTransformationRule> rules)
|
||||
@@ -32,6 +34,13 @@ public class RecordTransformationService : IRecordTransformationService
|
||||
|
||||
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.TargetField, out var targetProp)) return;
|
||||
|
||||
@@ -43,7 +52,7 @@ public class RecordTransformationService : IRecordTransformationService
|
||||
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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
public sealed class CopyTransformationStrategy : ITransformationStrategy
|
||||
{
|
||||
public string TransformationType => "Copy";
|
||||
public string Description => "Kopiert Source nach Target.";
|
||||
public object? Transform(object? sourceValue, string? argument) => sourceValue;
|
||||
}
|
||||
|
||||
public sealed class UppercaseTransformationStrategy : ITransformationStrategy
|
||||
{
|
||||
public string TransformationType => "Uppercase";
|
||||
public string Description => "Wandelt Text in Grossbuchstaben.";
|
||||
public object? Transform(object? sourceValue, string? argument) => sourceValue?.ToString()?.ToUpperInvariant();
|
||||
}
|
||||
|
||||
public sealed class LowercaseTransformationStrategy : ITransformationStrategy
|
||||
{
|
||||
public string TransformationType => "Lowercase";
|
||||
public string Description => "Wandelt Text in Kleinbuchstaben.";
|
||||
public object? Transform(object? sourceValue, string? argument) => sourceValue?.ToString()?.ToLowerInvariant();
|
||||
}
|
||||
|
||||
public sealed class PrefixTransformationStrategy : ITransformationStrategy
|
||||
{
|
||||
public string TransformationType => "Prefix";
|
||||
public string Description => "Stellt Argument vor den Source-Wert.";
|
||||
public object? Transform(object? sourceValue, string? argument) => $"{argument}{sourceValue}";
|
||||
}
|
||||
|
||||
public sealed class SuffixTransformationStrategy : ITransformationStrategy
|
||||
{
|
||||
public string TransformationType => "Suffix";
|
||||
public string Description => "Haengt Argument an den Source-Wert.";
|
||||
public object? Transform(object? sourceValue, string? argument) => $"{sourceValue}{argument}";
|
||||
}
|
||||
|
||||
public sealed class ReplaceTransformationStrategy : ITransformationStrategy
|
||||
{
|
||||
public string TransformationType => "Replace";
|
||||
public string Description => "Ersetzt in Text mit Syntax alt=>neu.";
|
||||
|
||||
public object? Transform(object? sourceValue, string? argument)
|
||||
{
|
||||
@@ -54,5 +62,54 @@ public sealed class ReplaceTransformationStrategy : ITransformationStrategy
|
||||
public sealed class ConstantTransformationStrategy : ITransformationStrategy
|
||||
{
|
||||
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 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