Add product division map fallback
This commit is contained in:
@@ -7,6 +7,8 @@ namespace TrafagSalesExporter.Services;
|
|||||||
|
|
||||||
public class DatabaseSeedService : IDatabaseSeedService
|
public class DatabaseSeedService : IDatabaseSeedService
|
||||||
{
|
{
|
||||||
|
private const string SpainSharePointFolder = "https://trafagag.sharepoint.com/sites/WorldwideBIPlatform/Import/Finance/Spanien";
|
||||||
|
|
||||||
public void SeedDefaults(AppDbContext db)
|
public void SeedDefaults(AppDbContext db)
|
||||||
{
|
{
|
||||||
SeedIfEmpty(db);
|
SeedIfEmpty(db);
|
||||||
@@ -529,6 +531,12 @@ public class DatabaseSeedService : IDatabaseSeedService
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ShouldRepairSpainManualImportPath(existing.ManualImportFilePath))
|
||||||
|
{
|
||||||
|
existing.ManualImportFilePath = SpainSharePointFolder;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
|
||||||
@@ -541,11 +549,22 @@ public class DatabaseSeedService : IDatabaseSeedService
|
|||||||
TSC = "TRES",
|
TSC = "TRES",
|
||||||
Land = "Spanien",
|
Land = "Spanien",
|
||||||
SourceSystem = "MANUAL_EXCEL",
|
SourceSystem = "MANUAL_EXCEL",
|
||||||
|
ManualImportFilePath = SpainSharePointFolder,
|
||||||
IsActive = false
|
IsActive = false
|
||||||
});
|
});
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool ShouldRepairSpainManualImportPath(string? path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var normalized = path.Trim().Replace('\\', '/');
|
||||||
|
return normalized.Contains("/Import/Finance/Spanien/Spain_Sales_", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
normalized.EndsWith("/Import/Finance/Spanien/Spain_Sales_2025.csv", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
private static void EnsureGermanyManualExcelSite(AppDbContext db)
|
private static void EnsureGermanyManualExcelSite(AppDbContext db)
|
||||||
{
|
{
|
||||||
if (db.Sites.Count() <= 1)
|
if (db.Sites.Count() <= 1)
|
||||||
@@ -912,7 +931,7 @@ public class DatabaseSeedService : IDatabaseSeedService
|
|||||||
}
|
}
|
||||||
|
|
||||||
var obsoleteSources = db.SapSourceDefinitions
|
var obsoleteSources = db.SapSourceDefinitions
|
||||||
.Where(x => x.SiteId == siteId && x.Alias != "Z" && x.Alias != "P")
|
.Where(x => x.SiteId == siteId && x.Alias != "Z" && x.Alias != "P" && x.Alias != "M")
|
||||||
.ToList();
|
.ToList();
|
||||||
foreach (var obsoleteSource in obsoleteSources)
|
foreach (var obsoleteSource in obsoleteSources)
|
||||||
{
|
{
|
||||||
@@ -973,6 +992,50 @@ public class DatabaseSeedService : IDatabaseSeedService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var productMapSource = db.SapSourceDefinitions
|
||||||
|
.OrderBy(x => x.Id)
|
||||||
|
.FirstOrDefault(x => x.SiteId == siteId && x.Alias == "M");
|
||||||
|
|
||||||
|
if (productMapSource is null)
|
||||||
|
{
|
||||||
|
db.SapSourceDefinitions.Add(new SapSourceDefinition
|
||||||
|
{
|
||||||
|
SiteId = siteId,
|
||||||
|
Alias = "M",
|
||||||
|
EntitySet = "ProductDivisionMapSet",
|
||||||
|
IsPrimary = false,
|
||||||
|
IsActive = true,
|
||||||
|
SortOrder = 2
|
||||||
|
});
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (productMapSource.EntitySet != "ProductDivisionMapSet")
|
||||||
|
{
|
||||||
|
productMapSource.EntitySet = "ProductDivisionMapSet";
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productMapSource.IsPrimary)
|
||||||
|
{
|
||||||
|
productMapSource.IsPrimary = false;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!productMapSource.IsActive)
|
||||||
|
{
|
||||||
|
productMapSource.IsActive = true;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productMapSource.SortOrder != 2)
|
||||||
|
{
|
||||||
|
productMapSource.SortOrder = 2;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var productJoin = db.SapJoinDefinitions
|
var productJoin = db.SapJoinDefinitions
|
||||||
.OrderBy(x => x.Id)
|
.OrderBy(x => x.Id)
|
||||||
.FirstOrDefault(x =>
|
.FirstOrDefault(x =>
|
||||||
@@ -1028,6 +1091,61 @@ public class DatabaseSeedService : IDatabaseSeedService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var productMapJoin = db.SapJoinDefinitions
|
||||||
|
.OrderBy(x => x.Id)
|
||||||
|
.FirstOrDefault(x =>
|
||||||
|
x.SiteId == siteId &&
|
||||||
|
x.LeftAlias == "Z" &&
|
||||||
|
x.RightAlias == "M");
|
||||||
|
|
||||||
|
if (productMapJoin is null)
|
||||||
|
{
|
||||||
|
db.SapJoinDefinitions.Add(new SapJoinDefinition
|
||||||
|
{
|
||||||
|
SiteId = siteId,
|
||||||
|
LeftAlias = "Z",
|
||||||
|
RightAlias = "M",
|
||||||
|
LeftKeys = "Prodh",
|
||||||
|
RightKeys = "Paph1",
|
||||||
|
JoinType = "Left",
|
||||||
|
IsActive = true,
|
||||||
|
SortOrder = 2
|
||||||
|
});
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (productMapJoin.LeftKeys != "Prodh")
|
||||||
|
{
|
||||||
|
productMapJoin.LeftKeys = "Prodh";
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productMapJoin.RightKeys != "Paph1")
|
||||||
|
{
|
||||||
|
productMapJoin.RightKeys = "Paph1";
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productMapJoin.JoinType != "Left")
|
||||||
|
{
|
||||||
|
productMapJoin.JoinType = "Left";
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!productMapJoin.IsActive)
|
||||||
|
{
|
||||||
|
productMapJoin.IsActive = true;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productMapJoin.SortOrder != 2)
|
||||||
|
{
|
||||||
|
productMapJoin.SortOrder = 2;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var mappings = new (string Target, string Source, bool Required)[]
|
var mappings = new (string Target, string Source, bool Required)[]
|
||||||
{
|
{
|
||||||
(nameof(SalesRecord.Tsc), "Z.Tsc", true),
|
(nameof(SalesRecord.Tsc), "Z.Tsc", true),
|
||||||
@@ -1040,13 +1158,13 @@ public class DatabaseSeedService : IDatabaseSeedService
|
|||||||
(nameof(SalesRecord.Material), "Z.Matnr", false),
|
(nameof(SalesRecord.Material), "Z.Matnr", false),
|
||||||
(nameof(SalesRecord.Name), "Z.Arktx", false),
|
(nameof(SalesRecord.Name), "Z.Arktx", false),
|
||||||
(nameof(SalesRecord.ProductGroup), "Z.Prodh", false),
|
(nameof(SalesRecord.ProductGroup), "Z.Prodh", false),
|
||||||
(nameof(SalesRecord.ProductHierarchyCode), "P.Paph1", false),
|
(nameof(SalesRecord.ProductHierarchyCode), "FirstNonEmpty(P.Paph1, M.Paph1)", false),
|
||||||
(nameof(SalesRecord.ProductHierarchyText), "P.Paph1Text", false),
|
(nameof(SalesRecord.ProductHierarchyText), "FirstNonEmpty(P.Paph1Text, M.Paph1Text)", false),
|
||||||
(nameof(SalesRecord.ProductFamilyCode), "P.Wwpfa", false),
|
(nameof(SalesRecord.ProductFamilyCode), "FirstNonEmpty(P.Wwpfa, M.Wwpfa)", false),
|
||||||
(nameof(SalesRecord.ProductFamilyText), "P.WwpfaText", false),
|
(nameof(SalesRecord.ProductFamilyText), "FirstNonEmpty(P.WwpfaText, M.WwpfaText)", false),
|
||||||
(nameof(SalesRecord.ProductDivisionCode), "P.Wwpsp", false),
|
(nameof(SalesRecord.ProductDivisionCode), "FirstNonEmpty(P.Wwpsp, M.Wwpsp)", false),
|
||||||
(nameof(SalesRecord.ProductDivisionText), "P.WwpspText", false),
|
(nameof(SalesRecord.ProductDivisionText), "FirstNonEmpty(P.WwpspText, M.WwpspText)", false),
|
||||||
(nameof(SalesRecord.ProductMappingAssigned), "P.IsAssigned", false),
|
(nameof(SalesRecord.ProductMappingAssigned), "FirstNonEmpty(P.IsAssigned, M.IsAssigned)", false),
|
||||||
(nameof(SalesRecord.Quantity), "Z.Fkimg", false),
|
(nameof(SalesRecord.Quantity), "Z.Fkimg", false),
|
||||||
(nameof(SalesRecord.CustomerNumber), "Z.Kunnr", false),
|
(nameof(SalesRecord.CustomerNumber), "Z.Kunnr", false),
|
||||||
(nameof(SalesRecord.CustomerName), "Z.Name1", false),
|
(nameof(SalesRecord.CustomerName), "Z.Name1", false),
|
||||||
|
|||||||
@@ -129,12 +129,54 @@ public sealed class MappedSalesRecordComposer : IMappedSalesRecordComposer
|
|||||||
if (value.StartsWith('='))
|
if (value.StartsWith('='))
|
||||||
return value[1..];
|
return value[1..];
|
||||||
|
|
||||||
|
if (TryEvaluateFirstNonEmpty(row, value, out var firstNonEmpty))
|
||||||
|
return firstNonEmpty;
|
||||||
|
|
||||||
if (row.TryGetValue(value, out var direct))
|
if (row.TryGetValue(value, out var direct))
|
||||||
return direct;
|
return direct;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryEvaluateFirstNonEmpty(Dictionary<string, object?> row, string expression, out object? result)
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
const string functionName = "FirstNonEmpty";
|
||||||
|
if (!expression.StartsWith(functionName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var openParen = expression.IndexOf('(');
|
||||||
|
var closeParen = expression.LastIndexOf(')');
|
||||||
|
if (openParen < functionName.Length || closeParen <= openParen)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var arguments = expression[(openParen + 1)..closeParen]
|
||||||
|
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||||
|
|
||||||
|
foreach (var argument in arguments)
|
||||||
|
{
|
||||||
|
var value = EvaluateExpression(row, argument);
|
||||||
|
if (!IsEmptyValue(value))
|
||||||
|
{
|
||||||
|
result = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsEmptyValue(object? value)
|
||||||
|
{
|
||||||
|
if (value is null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (value is string text)
|
||||||
|
return string.IsNullOrWhiteSpace(text);
|
||||||
|
|
||||||
|
return string.IsNullOrWhiteSpace(value.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
private static void ApplyValue(SalesRecord record, string targetField, object? value)
|
private static void ApplyValue(SalesRecord record, string targetField, object? value)
|
||||||
{
|
{
|
||||||
var property = typeof(SalesRecord).GetProperty(targetField);
|
var property = typeof(SalesRecord).GetProperty(targetField);
|
||||||
|
|||||||
@@ -135,6 +135,23 @@ public class DatabaseInitializationServiceTests : IDisposable
|
|||||||
Assert.Equal(sageServer.Id, india.HanaServerId);
|
Assert.Equal(sageServer.Id, india.HanaServerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task InitializeAsync_Repairs_Spain_Manual_Import_File_To_Folder()
|
||||||
|
{
|
||||||
|
await PrepareSpainManualImportFilePathAsync();
|
||||||
|
|
||||||
|
var service = CreateService();
|
||||||
|
await service.InitializeAsync();
|
||||||
|
|
||||||
|
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||||
|
var spain = Assert.Single(db.Sites, x => x.TSC == "TRSE");
|
||||||
|
|
||||||
|
Assert.Equal("MANUAL_EXCEL", spain.SourceSystem);
|
||||||
|
Assert.Equal(
|
||||||
|
"https://trafagag.sharepoint.com/sites/WorldwideBIPlatform/Import/Finance/Spanien",
|
||||||
|
spain.ManualImportFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task PrepareLegacySitesTableAsync()
|
private async Task PrepareLegacySitesTableAsync()
|
||||||
{
|
{
|
||||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||||
@@ -240,6 +257,35 @@ VALUES (
|
|||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task PrepareSpainManualImportFilePathAsync()
|
||||||
|
{
|
||||||
|
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||||
|
|
||||||
|
db.HanaServers.RemoveRange(db.HanaServers);
|
||||||
|
db.Sites.RemoveRange(db.Sites);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
db.Sites.AddRange(
|
||||||
|
new Site
|
||||||
|
{
|
||||||
|
Schema = "fr01_p",
|
||||||
|
TSC = "TRFR",
|
||||||
|
Land = "Frankreich",
|
||||||
|
SourceSystem = "BI1",
|
||||||
|
IsActive = true
|
||||||
|
},
|
||||||
|
new Site
|
||||||
|
{
|
||||||
|
Schema = "Spanien",
|
||||||
|
TSC = "TRSE",
|
||||||
|
Land = "Spanien",
|
||||||
|
SourceSystem = "MANUAL_EXCEL",
|
||||||
|
ManualImportFilePath = "https://trafagag.sharepoint.com/sites/WorldwideBIPlatform/Import/Finance/Spanien/Spain_Sales_2025.csv",
|
||||||
|
IsActive = true
|
||||||
|
});
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task PrepareBrokenHanaServerForeignKeyAsync()
|
private async Task PrepareBrokenHanaServerForeignKeyAsync()
|
||||||
{
|
{
|
||||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||||
|
|||||||
@@ -108,6 +108,126 @@ public class MappedSalesRecordComposerTests
|
|||||||
Assert.Equal("HANA", result[0].DocumentType);
|
Assert.Equal("HANA", result[0].DocumentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Compose_UsesFirstNonEmptyFallbackWhenPrimaryProductReferenceIsMissing()
|
||||||
|
{
|
||||||
|
var composer = new MappedSalesRecordComposer();
|
||||||
|
var site = new Site { TSC = "TRAT", Land = "Oesterreich" };
|
||||||
|
var sources = new[]
|
||||||
|
{
|
||||||
|
new SapSourceDefinition { Alias = "Z", EntitySet = "Sales", IsPrimary = true, IsActive = true },
|
||||||
|
new SapSourceDefinition { Alias = "P", EntitySet = "ProductDivisionRefSet", IsActive = true, SortOrder = 1 },
|
||||||
|
new SapSourceDefinition { Alias = "M", EntitySet = "ProductDivisionMapSet", IsActive = true, SortOrder = 2 }
|
||||||
|
};
|
||||||
|
var joins = new[]
|
||||||
|
{
|
||||||
|
new SapJoinDefinition { LeftAlias = "Z", RightAlias = "P", LeftKeys = "Matnr", RightKeys = "Matnr", IsActive = true, SortOrder = 1 },
|
||||||
|
new SapJoinDefinition { LeftAlias = "Z", RightAlias = "M", LeftKeys = "Prodh", RightKeys = "Paph1", IsActive = true, SortOrder = 2 }
|
||||||
|
};
|
||||||
|
var mappings = new[]
|
||||||
|
{
|
||||||
|
Mapping(nameof(SalesRecord.Material), "Z.Matnr"),
|
||||||
|
Mapping(nameof(SalesRecord.ProductGroup), "Z.Prodh"),
|
||||||
|
Mapping(nameof(SalesRecord.ProductHierarchyCode), "FirstNonEmpty(P.Paph1, M.Paph1)"),
|
||||||
|
Mapping(nameof(SalesRecord.ProductFamilyCode), "FirstNonEmpty(P.Wwpfa, M.Wwpfa)"),
|
||||||
|
Mapping(nameof(SalesRecord.ProductDivisionCode), "FirstNonEmpty(P.Wwpsp, M.Wwpsp)"),
|
||||||
|
Mapping(nameof(SalesRecord.ProductMappingAssigned), "FirstNonEmpty(P.IsAssigned, M.IsAssigned)")
|
||||||
|
};
|
||||||
|
var rows = new Dictionary<string, List<Dictionary<string, object?>>>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["Z"] =
|
||||||
|
[
|
||||||
|
new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["Matnr"] = "900720",
|
||||||
|
["Prodh"] = "9999"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
["P"] = [],
|
||||||
|
["M"] =
|
||||||
|
[
|
||||||
|
new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["Paph1"] = "9999",
|
||||||
|
["Wwpfa"] = "0043",
|
||||||
|
["Wwpsp"] = "0008",
|
||||||
|
["IsAssigned"] = true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = composer.Compose(site, sources, joins, mappings, rows, "SAP");
|
||||||
|
|
||||||
|
Assert.Single(result);
|
||||||
|
Assert.Equal("900720", result[0].Material);
|
||||||
|
Assert.Equal("9999", result[0].ProductGroup);
|
||||||
|
Assert.Equal("9999", result[0].ProductHierarchyCode);
|
||||||
|
Assert.Equal("0043", result[0].ProductFamilyCode);
|
||||||
|
Assert.Equal("0008", result[0].ProductDivisionCode);
|
||||||
|
Assert.Equal("True", result[0].ProductMappingAssigned);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Compose_FirstNonEmptyKeepsMaterialReferenceBeforeProductHierarchyFallback()
|
||||||
|
{
|
||||||
|
var composer = new MappedSalesRecordComposer();
|
||||||
|
var site = new Site { TSC = "TRCH", Land = "Schweiz" };
|
||||||
|
var sources = new[]
|
||||||
|
{
|
||||||
|
new SapSourceDefinition { Alias = "Z", EntitySet = "Sales", IsPrimary = true, IsActive = true },
|
||||||
|
new SapSourceDefinition { Alias = "P", EntitySet = "ProductDivisionRefSet", IsActive = true, SortOrder = 1 },
|
||||||
|
new SapSourceDefinition { Alias = "M", EntitySet = "ProductDivisionMapSet", IsActive = true, SortOrder = 2 }
|
||||||
|
};
|
||||||
|
var joins = new[]
|
||||||
|
{
|
||||||
|
new SapJoinDefinition { LeftAlias = "Z", RightAlias = "P", LeftKeys = "Matnr", RightKeys = "Matnr", IsActive = true, SortOrder = 1 },
|
||||||
|
new SapJoinDefinition { LeftAlias = "Z", RightAlias = "M", LeftKeys = "Prodh", RightKeys = "Paph1", IsActive = true, SortOrder = 2 }
|
||||||
|
};
|
||||||
|
var mappings = new[]
|
||||||
|
{
|
||||||
|
Mapping(nameof(SalesRecord.ProductHierarchyCode), "FirstNonEmpty(P.Paph1, M.Paph1)"),
|
||||||
|
Mapping(nameof(SalesRecord.ProductFamilyCode), "FirstNonEmpty(P.Wwpfa, M.Wwpfa)"),
|
||||||
|
Mapping(nameof(SalesRecord.ProductDivisionCode), "FirstNonEmpty(P.Wwpsp, M.Wwpsp)")
|
||||||
|
};
|
||||||
|
var rows = new Dictionary<string, List<Dictionary<string, object?>>>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["Z"] =
|
||||||
|
[
|
||||||
|
new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["Matnr"] = "6",
|
||||||
|
["Prodh"] = "9999"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
["P"] =
|
||||||
|
[
|
||||||
|
new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["Matnr"] = "6",
|
||||||
|
["Paph1"] = "0414",
|
||||||
|
["Wwpfa"] = "0004",
|
||||||
|
["Wwpsp"] = "0001"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
["M"] =
|
||||||
|
[
|
||||||
|
new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["Paph1"] = "9999",
|
||||||
|
["Wwpfa"] = "0043",
|
||||||
|
["Wwpsp"] = "0008"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = composer.Compose(site, sources, joins, mappings, rows, "SAP");
|
||||||
|
|
||||||
|
Assert.Single(result);
|
||||||
|
Assert.Equal("0414", result[0].ProductHierarchyCode);
|
||||||
|
Assert.Equal("0004", result[0].ProductFamilyCode);
|
||||||
|
Assert.Equal("0001", result[0].ProductDivisionCode);
|
||||||
|
}
|
||||||
|
|
||||||
private static SapFieldMapping Mapping(string targetField, string sourceExpression)
|
private static SapFieldMapping Mapping(string targetField, string sourceExpression)
|
||||||
=> new()
|
=> new()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,83 @@
|
|||||||
|
|
||||||
Letzter Nachtrag: 2026-06-10
|
Letzter Nachtrag: 2026-06-10
|
||||||
|
|
||||||
|
## Nachtrag 2026-06-10 Deploy Produktsparten-Fallback `ProductDivisionMapSet`
|
||||||
|
|
||||||
|
Durchgefuehrt:
|
||||||
|
|
||||||
|
- Release-Publish aus `TrafagSalesExporter` nach:
|
||||||
|
|
||||||
|
```text
|
||||||
|
\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\
|
||||||
|
```
|
||||||
|
|
||||||
|
- Befehl:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
dotnet publish .\TrafagSalesExporter.csproj -c Release --no-restore /p:PublishProfile=FolderProfile --verbosity minimal
|
||||||
|
```
|
||||||
|
|
||||||
|
- App wurde fuer Publish und Server-DB-Aktualisierung per `app_offline.htm` gestoppt und danach wieder online geschaltet.
|
||||||
|
|
||||||
|
Deploy-Inhalt:
|
||||||
|
|
||||||
|
- Neuer SAP-OData-Fallback fuer CH/AT:
|
||||||
|
- Quelle `M = ProductDivisionMapSet`
|
||||||
|
- Join `Z.Prodh = M.Paph1`
|
||||||
|
- Produktfelder nutzen `FirstNonEmpty(P.*, M.*)`
|
||||||
|
- Materialbasierter Treffer aus `ProductDivisionRefSet` bleibt fuehrend.
|
||||||
|
- Wenn die Materialreferenz fehlt, aber `Z.Prodh` gefuellt ist, wird die flache `PAPH1 -> WWPFA -> WWPSP`-Map genutzt.
|
||||||
|
- Wenn auch `Z.Prodh` leer ist, bleibt die Zeile ohne Produktreferenz; dafuer gibt es keinen technischen Schluessel.
|
||||||
|
- Spain-Seed-Reparatur bleibt enthalten: `TRSE` zeigt auf den SharePoint-Ordner fuer Basis- und Range-/Delta-CSV.
|
||||||
|
|
||||||
|
Share-/DB-Pruefung:
|
||||||
|
|
||||||
|
- `BiDashboard.dll` Zeitstempel nach Deploy: `10.06.2026 16:09:44`.
|
||||||
|
- `app_offline.htm` wurde entfernt.
|
||||||
|
- Server-DB-Backup vor Seed/Import:
|
||||||
|
|
||||||
|
```text
|
||||||
|
\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\trafag_exporter.db.before-productdivision-map-20260610-161022.bak
|
||||||
|
```
|
||||||
|
|
||||||
|
- Server-DB nach Seed:
|
||||||
|
- `ZSCHWEIZ` Quellen: `Z:FinanzdataSchweizOeSet`, `P:ProductDivisionRefSet`, `M:ProductDivisionMapSet`
|
||||||
|
- Joins: `Z.Matnr = P.Matnr`, `Z.Prodh = M.Paph1`
|
||||||
|
- Produkt-Mappings: `FirstNonEmpty(P.*, M.*)`
|
||||||
|
|
||||||
|
CH/AT-Import auf Server-DB:
|
||||||
|
|
||||||
|
```text
|
||||||
|
FetchedRecords = 40'292
|
||||||
|
Assigned = 36'953
|
||||||
|
UnassignedWithReference = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Aufteilung nach TSC:
|
||||||
|
|
||||||
|
```text
|
||||||
|
TRCH Rows 38'838, Assigned 35'526, NoReferenceFields 3'312
|
||||||
|
TRAT Rows 1'454, Assigned 1'427, NoReferenceFields 27
|
||||||
|
```
|
||||||
|
|
||||||
|
Validierung:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
dotnet test TrafagSalesExporter.sln --verbosity minimal
|
||||||
|
```
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
```text
|
||||||
|
87/87 Tests gruen
|
||||||
|
```
|
||||||
|
|
||||||
|
SAP-Gateway-Vorbedingung:
|
||||||
|
|
||||||
|
- `ProductDivisionMapSet` ist im Service `ZPOWERBI_EINKAUF_SRV` aktiv.
|
||||||
|
- `$metadata` enthaelt `ProductDivisionMap` mit `WwpfaText`.
|
||||||
|
- `ProductDivisionMapSet` liefert 1'296 Zeilen.
|
||||||
|
|
||||||
## Nachtrag 2026-06-10 Deploy India / SAGE HANA Mapping
|
## Nachtrag 2026-06-10 Deploy India / SAGE HANA Mapping
|
||||||
|
|
||||||
Durchgefuehrt:
|
Durchgefuehrt:
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ Bereinigung 2026-06-09:
|
|||||||
- Historische Finance-Stubs und der alte Finance-Handoff wurden aus der aktiven Doku entfernt, weil der Volltext im Raw-Archiv liegt.
|
- Historische Finance-Stubs und der alte Finance-Handoff wurden aus der aktiven Doku entfernt, weil der Volltext im Raw-Archiv liegt.
|
||||||
- Die alte deutsche Spanien-rclone-Anleitung wurde entfernt, weil der aktuelle All-in-one-Workflow im Guide vom 2026-06-05 dokumentiert ist.
|
- Die alte deutsche Spanien-rclone-Anleitung wurde entfernt, weil der aktuelle All-in-one-Workflow im Guide vom 2026-06-05 dokumentiert ist.
|
||||||
- Die Alphaplan-Konzept- und Anleitungsdateien vom 2026-06-08 wurden bewusst nicht veraendert.
|
- Die Alphaplan-Konzept- und Anleitungsdateien vom 2026-06-08 wurden bewusst nicht veraendert.
|
||||||
- Delta 2026-06-10: India/SAGE-HANA-Deploy und Server-DB-Seed wurden in `docs/DEPLOYMENT_IIS_HANDOFF_2026-05-19.md` und `lastchange.md` nachdokumentiert.
|
- Delta 2026-06-10: Produktsparten-Fallback `ProductDivisionMapSet`, India/SAGE-HANA-Deploy und Server-DB-Seeds wurden in `docs/DEPLOYMENT_IIS_HANDOFF_2026-05-19.md`, `spartenlogic/UEBERGABE_PRODUKTSPARTEN_ZUORDNUNG.md`, `docs/rag/DEPLOYMENT.md`, `docs/rag/PROJECT.md` und `lastchange.md` nachdokumentiert.
|
||||||
|
|
||||||
Weiterhin gilt:
|
Weiterhin gilt:
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ Stand: 2026-06-10
|
|||||||
## Kurzstand
|
## Kurzstand
|
||||||
|
|
||||||
- `TrafagSalesExporter` wird als ASP.NET/IIS-Webanwendung im bisherigen `BiDashboard`-Schema publiziert.
|
- `TrafagSalesExporter` wird als ASP.NET/IIS-Webanwendung im bisherigen `BiDashboard`-Schema publiziert.
|
||||||
- Letzter dokumentierter Deploy: 2026-06-10 India/SAGE-HANA-Fix.
|
- Letzter dokumentierter Deploy: 2026-06-10 Produktsparten-Fallback `ProductDivisionMapSet`.
|
||||||
- Publish-Ziel: `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\`.
|
- Publish-Ziel: `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\`.
|
||||||
- Letzter Deploy-Zeitstempel: `BiDashboard.dll` am `10.06.2026 08:20:25`.
|
- Letzter Deploy-Zeitstempel: `BiDashboard.dll` am `10.06.2026 16:09:44`.
|
||||||
|
- Produktive CH/AT-DB-Konfiguration nach Seed: `ZSCHWEIZ` Quellen `Z:FinanzdataSchweizOeSet`, `P:ProductDivisionRefSet`, `M:ProductDivisionMapSet`; Joins `Z.Matnr=P.Matnr` und `Z.Prodh=M.Paph1`.
|
||||||
|
- CH/AT-Import nach Deploy: `FetchedRecords=40'292`, `Assigned=36'953`, `UnassignedWithReference=0`.
|
||||||
|
- DB-Backup vor Produktsparten-Seed/Import: `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\trafag_exporter.db.before-productdivision-map-20260610-161022.bak`.
|
||||||
- Produktive India-DB-Konfiguration nach Seed: `TRIN -> SAGE -> 20.197.20.60:30015`, Schema `TRAFAG_LIVE`, User-Override `TRAFAGCONTROLS`.
|
- Produktive India-DB-Konfiguration nach Seed: `TRIN -> SAGE -> 20.197.20.60:30015`, Schema `TRAFAG_LIVE`, User-Override `TRAFAGCONTROLS`.
|
||||||
- DB-Backup vor India-Seed: `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\trafag_exporter.db.before-india-sage-20260610-0825.bak`.
|
- DB-Backup vor India-Seed: `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\trafag_exporter.db.before-india-sage-20260610-0825.bak`.
|
||||||
- Lokaler Uebergangsserver: `http://172.16.9.185:5000` im Trafag-Netz, IP kann wechseln.
|
- Lokaler Uebergangsserver: `http://172.16.9.185:5000` im Trafag-Netz, IP kann wechseln.
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ Stand: 2026-06-10
|
|||||||
## Kurzstand
|
## Kurzstand
|
||||||
|
|
||||||
- Fuehrende App: `TrafagSalesExporter`, publiziert als `BiDashboard`.
|
- Fuehrende App: `TrafagSalesExporter`, publiziert als `BiDashboard`.
|
||||||
- Letzter dokumentierter Stand: India/TRIN SAGE-HANA-Fix deployed, Finance-Kursworkflow dokumentiert, aktive Markdown-Doku bereinigt.
|
- Letzter dokumentierter Stand: CH/AT-Produktsparten-Fallback ueber `ProductDivisionMapSet` deployed; India/TRIN SAGE-HANA-Fix und Spanien-SharePoint-Pfad bleiben abgesichert.
|
||||||
- Validierung laut Doku: `84/84` Tests gruen fuer den India-Fix; fruehere UI-/Deploy-Schritte wurden einzeln umgesetzt und deployed.
|
- Validierung laut Doku: `87/87` Tests gruen fuer den Produktsparten-Fallback; fruehere UI-/Deploy-Schritte wurden einzeln umgesetzt und deployed.
|
||||||
- Letzter dokumentierter Deploy: 2026-06-10 auf `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\`.
|
- Letzter dokumentierter Deploy: 2026-06-10 auf `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\`.
|
||||||
|
- Produktsparten CH/AT: `ProductDivisionRefSet` bleibt materialbasiert fuehrend; Fallback `ProductDivisionMapSet` joined `Z.Prodh = M.Paph1`. Server-Import: 40'292 CH/AT-Datensaetze, 36'953 assigned, 0 `UnassignedWithReference`.
|
||||||
- India/TRIN: produktive Server-DB steht auf `TRIN -> SAGE -> 20.197.20.60:30015`, Schema `TRAFAG_LIVE`, User-Override `TRAFAGCONTROLS`.
|
- India/TRIN: produktive Server-DB steht auf `TRIN -> SAGE -> 20.197.20.60:30015`, Schema `TRAFAG_LIVE`, User-Override `TRAFAGCONTROLS`.
|
||||||
- Doku-Delta: `docs/FINANCE_KURS_WORKFLOW_2026-06-09.md` plus SVG; alte Finance-Stubs aus aktiver Markdown-Struktur entfernt, Volltexte bleiben im Raw-Archiv.
|
- Doku-Delta: `docs/FINANCE_KURS_WORKFLOW_2026-06-09.md` plus SVG; alte Finance-Stubs aus aktiver Markdown-Struktur entfernt, Volltexte bleiben im Raw-Archiv.
|
||||||
- Neu im Finance/Management-Cockpit: einfache Schnelluebersicht links sichtbar; tiefere Funktionen bleiben unter `Experten`.
|
- Neu im Finance/Management-Cockpit: einfache Schnelluebersicht links sichtbar; tiefere Funktionen bleiben unter `Experten`.
|
||||||
|
|||||||
@@ -8,14 +8,17 @@ Diese Datei ist fuer tokenarme RAG-Nutzung komprimiert.
|
|||||||
|
|
||||||
- Fuehrender Kurzkontext: `docs/rag/PROJECT.md`.
|
- Fuehrender Kurzkontext: `docs/rag/PROJECT.md`.
|
||||||
- Themenrouter: `docs/RAG_ROUTER.md`.
|
- Themenrouter: `docs/RAG_ROUTER.md`.
|
||||||
- Letzter dokumentierter Code-Stand: India/TRIN HANA-Route gegen SourceSystem-Drift abgesichert; Finance-Kursworkflow und Markdown-Bereinigung dokumentiert.
|
- Letzter dokumentierter Code-Stand: CH/AT-Produktsparten-Fallback ueber `ProductDivisionMapSet` deployed; India/TRIN HANA-Route und Spanien-SharePoint-Pfad bleiben im Seed abgesichert.
|
||||||
- Letzte dokumentierte Validierung: `dotnet test TrafagSalesExporter.Tests\TrafagSalesExporter.Tests.csproj --verbosity minimal` mit `84/84` Tests gruen.
|
- Letzte dokumentierte Validierung: `dotnet test TrafagSalesExporter.sln --verbosity minimal` mit `87/87` Tests gruen.
|
||||||
- Letzter dokumentierter Deploy: 2026-06-10 India/SAGE-HANA-Fix nach `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\`.
|
- Letzter dokumentierter Deploy: 2026-06-10 Produktsparten-Fallback nach `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\`.
|
||||||
|
- Neu umgesetzt und deployed: `ZSCHWEIZ` nutzt zusaetzlich `M = ProductDivisionMapSet` und den Join `Z.Prodh = M.Paph1`; Produktfelder fallen per `FirstNonEmpty(P.*, M.*)` von Materialreferenz auf PAPH1-Mapping zurueck.
|
||||||
|
- Server-DB am 2026-06-10 aktualisiert: CH/AT neu importiert, `FetchedRecords=40'292`, `Assigned=36'953`, `UnassignedWithReference=0`; Backup: `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\trafag_exporter.db.before-productdivision-map-20260610-161022.bak`.
|
||||||
|
- Deploy-Status 2026-06-10: `BiDashboard.dll` Zeitstempel `10.06.2026 16:09:44`; `app_offline.htm` wurde entfernt.
|
||||||
- Neu umgesetzt und deployed: `TRIN`/Indien wird beim Seed auf `SourceSystem=SAGE`, Schema `TRAFAG_LIVE` und zentralen SAGE-HANA-Server `20.197.20.60:30015` repariert; Standort-User-/Passwort-Override bleibt erhalten.
|
- Neu umgesetzt und deployed: `TRIN`/Indien wird beim Seed auf `SourceSystem=SAGE`, Schema `TRAFAG_LIVE` und zentralen SAGE-HANA-Server `20.197.20.60:30015` repariert; Standort-User-/Passwort-Override bleibt erhalten.
|
||||||
- Server-DB am 2026-06-10 korrigiert: `TRIN -> SAGE -> 20.197.20.60:30015`, User-Override `TRAFAGCONTROLS`, Passwort-Override vorhanden. Backup: `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\trafag_exporter.db.before-india-sage-20260610-0825.bak`.
|
- Server-DB am 2026-06-10 korrigiert: `TRIN -> SAGE -> 20.197.20.60:30015`, User-Override `TRAFAGCONTROLS`, Passwort-Override vorhanden. Backup: `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\trafag_exporter.db.before-india-sage-20260610-0825.bak`.
|
||||||
- Deploy-Status 2026-06-10: `BiDashboard.dll` Zeitstempel `10.06.2026 08:20:25`; `app_offline.htm` wurde entfernt.
|
- Server-DB am 2026-06-10 korrigiert: Spanien (`TRSE`) zeigt im manuellen Import jetzt auf den SharePoint-Ordner `https://trafagag.sharepoint.com/sites/WorldwideBIPlatform/Import/Finance/Spanien` statt auf die Einzeldatei `Spain_Sales_2025.csv`, damit Basis- und Range-/Delta-CSV zusammen gelesen werden. Backup: `\\trch-webapp-bidashboard.trafagch.local\BiDashboard$\trafag_exporter.db.before-spain-folder-path-20260610-100627.bak`.
|
||||||
- Git-Commit India-Fix: `586adc3 Fix India SAGE HANA mapping`.
|
- Git-Commit India-Fix: `586adc3 Fix India SAGE HANA mapping`.
|
||||||
- Neu dokumentiert: Delta zum India-Deploy in `docs/DEPLOYMENT_IIS_HANDOFF_2026-05-19.md`.
|
- Neu dokumentiert: Delta zum Produktsparten-Fallback-Deploy in `docs/DEPLOYMENT_IIS_HANDOFF_2026-05-19.md` und `spartenlogic/UEBERGABE_PRODUKTSPARTEN_ZUORDNUNG.md`.
|
||||||
- Neu lokal: Sparten-Finanzanalyse gruppiert standardmaessig nach `Produktsparte`; `Produktfamilie` und `PAPH1 Detail` bleiben als Umschaltoptionen erhalten.
|
- Neu lokal: Sparten-Finanzanalyse gruppiert standardmaessig nach `Produktsparte`; `Produktfamilie` und `PAPH1 Detail` bleiben als Umschaltoptionen erhalten.
|
||||||
- Neu lokal: Sparten-Finanzanalyse zeigt bei `Mixed`-Waehrung einen Warnhinweis, weil Summen/Anteile ueber mehrere Waehrungen fachlich nur eingeschraenkt belastbar sind.
|
- Neu lokal: Sparten-Finanzanalyse zeigt bei `Mixed`-Waehrung einen Warnhinweis, weil Summen/Anteile ueber mehrere Waehrungen fachlich nur eingeschraenkt belastbar sind.
|
||||||
- Neu lokal: Sparten-Finanzanalyse zeigt die groessten Treiber fuer `Nicht im TR-AG-Stamm`, damit hohe nicht zugeordnete Umsaetze nach Land/TSC/Material analysiert werden koennen.
|
- Neu lokal: Sparten-Finanzanalyse zeigt die groessten Treiber fuer `Nicht im TR-AG-Stamm`, damit hohe nicht zugeordnete Umsaetze nach Land/TSC/Material analysiert werden koennen.
|
||||||
|
|||||||
@@ -0,0 +1,486 @@
|
|||||||
|
# Uebergabe: Produktsparten-Zuordnung Trafag AG
|
||||||
|
|
||||||
|
Stand: 2026-06-10
|
||||||
|
|
||||||
|
## Ziel
|
||||||
|
|
||||||
|
Fuer das Group-Sales-Dashboard wird eine flache Referenztabelle benoetigt:
|
||||||
|
|
||||||
|
`Materialnummer -> Produkthierarchie PAPH1 -> Produktfamilie WWPFA -> Produktsparte WWPSP`
|
||||||
|
|
||||||
|
SAP/Trafag AG bleibt fuehrend. Das Dashboard soll keine Ableitungslogik nachbauen, sondern nur per Material bzw. PAPH1 nachschlagen. Nicht zuordenbare Artikel laufen im Dashboard als "Nicht zugeordnet".
|
||||||
|
|
||||||
|
## Finale Architektur
|
||||||
|
|
||||||
|
Die fuehrende fachliche Quelle ist KEDE/KEDR, nicht Excel und nicht das Dashboard.
|
||||||
|
|
||||||
|
Finaler technischer Ablauf:
|
||||||
|
|
||||||
|
1. `Z_PRODSPARTE_MAP_BUILD` liest die KEDE/KEDR-Regeltabellen direkt.
|
||||||
|
2. Die Von-bis-Regeln werden in einzelne PAPH1-Werte expandiert.
|
||||||
|
3. Das Ergebnis wird in `ZPRODSPARTE_MAP` als flache Tabelle geschrieben:
|
||||||
|
`PAPH1 -> WWPFA -> WWPSP`
|
||||||
|
4. `ZCL_PRODSPARTE_PROVIDER=>GET_DATA` liest `MVKE`/`MAKT` und macht einen exakten Lookup auf `ZPRODSPARTE_MAP`.
|
||||||
|
5. `Z_PRODSPARTE_ALL` und der OData-Service rufen nur noch den Provider.
|
||||||
|
|
||||||
|
Damit bleibt die Laufzeitlogik einfach: kein CSV-Import im produktiven Ablauf, keine Excel-Abhaengigkeit, keine Von-bis-Aufloesung im Dashboard.
|
||||||
|
|
||||||
|
## Warum die Architektur geaendert wurde
|
||||||
|
|
||||||
|
Der erste Ansatz war, die bereits abgeleiteten Werte direkt aus `CE11000` je Material zu lesen. Das war fuer verkaufte Artikel plausibel, aber in der Praxis nicht vollstaendig.
|
||||||
|
|
||||||
|
Test mit `sapdataexport.csv`:
|
||||||
|
|
||||||
|
- SAP-Materialzeilen: 42'232
|
||||||
|
- Zugeordnet: 34'462
|
||||||
|
- Nicht zugeordnet: 7'770
|
||||||
|
- Davon laut Excel/KEDE-Regeln fachlich abgedeckt: 7'768
|
||||||
|
- Uebrige echte Luecke: `PAPH1 = 8950`
|
||||||
|
|
||||||
|
Schlussfolgerung:
|
||||||
|
|
||||||
|
`CE11000` reicht als primaere Quelle nicht aus, weil Materialien ohne passende CO-PA-Belegableitung oder mit anderer Beleglage fehlen koennen. Die Regeln muessen aus KEDE/KEDR kommen.
|
||||||
|
|
||||||
|
## Verifizierte SAP-Quellen
|
||||||
|
|
||||||
|
Systemkontext:
|
||||||
|
|
||||||
|
- S/4HANA, S4CORE 108
|
||||||
|
- Ergebnisbereich: `ERKRS = 1000`
|
||||||
|
- KEDR-Strategie: `DERI`
|
||||||
|
- Applikationsklasse: `KE`
|
||||||
|
- Fuehrende Pflege in KEDE/KEDR
|
||||||
|
|
||||||
|
Verifizierte Tabellen aus `TKEDRS`:
|
||||||
|
|
||||||
|
- Schritt `0028`
|
||||||
|
- `METHOD = DRULE`
|
||||||
|
- `KEDRENV = 1000`
|
||||||
|
- `PARAM_1 = K9RT761000002`
|
||||||
|
- Bedeutung: `PAPH1 von-bis -> WWPFA`
|
||||||
|
- Schritt `0031`
|
||||||
|
- `METHOD = DRULE`
|
||||||
|
- `KEDRENV = 1000`
|
||||||
|
- `PARAM_1 = K9RT761000003`
|
||||||
|
- Bedeutung: `WWPFA von-bis -> WWPSP`
|
||||||
|
|
||||||
|
Feldstruktur `K9RT761000002`:
|
||||||
|
|
||||||
|
- `SOUR1_FROM` CHAR5: ProdHierarchie01-1 von
|
||||||
|
- `SOUR1_TO` CHAR5: ProdHierarchie01-1 bis
|
||||||
|
- `VALID_FROM` DATS
|
||||||
|
- `TARGET1` CHAR6: Produktfamilie
|
||||||
|
- `DELETE_FLG`
|
||||||
|
- `ADDED_BY`
|
||||||
|
- `ADDED_ON`
|
||||||
|
|
||||||
|
Feldstruktur `K9RT761000003`:
|
||||||
|
|
||||||
|
- `SOUR1_FROM` CHAR6: Produktfamilie von
|
||||||
|
- `SOUR1_TO` CHAR6: Produktfamilie bis
|
||||||
|
- `VALID_FROM` DATS
|
||||||
|
- `TARGET1` CHAR6: Produktsparte
|
||||||
|
- `DELETE_FLG`
|
||||||
|
- `ADDED_BY`
|
||||||
|
- `ADDED_ON`
|
||||||
|
|
||||||
|
Weitere relevante Tabellen:
|
||||||
|
|
||||||
|
- `MVKE-PRODH`: Produkthierarchie am Material, CHAR18, bei Trafag fachlich 4-stellig gepflegt
|
||||||
|
- `MAKT`: Materialkurztext
|
||||||
|
- `T179T`: Text zur Produkthierarchie
|
||||||
|
- `T25A0`: Texte Produktfamilie `WWPFA`
|
||||||
|
- `T25A1`: Texte Produktsparte `WWPSP`
|
||||||
|
- `ZPRODSPARTE_MAP`: flache Mapping-Tabelle `PAPH1 -> WWPFA -> WWPSP`
|
||||||
|
|
||||||
|
## Von-bis-Logik
|
||||||
|
|
||||||
|
KEDE pflegt die Produktfamilie nicht nur als Einzelwerte, sondern als Bereiche.
|
||||||
|
|
||||||
|
Beispiele:
|
||||||
|
|
||||||
|
- `0104` bis `0199` -> `WWPFA = 0001`
|
||||||
|
- `8412` bis `8413` -> `WWPFA = 0028`
|
||||||
|
- `8280` ohne bis -> Einzelwert `8280` -> `WWPFA = 0032`
|
||||||
|
|
||||||
|
Regeln:
|
||||||
|
|
||||||
|
- Wenn `SOUR1_TO` leer ist, gilt die Zeile als Einzelwert.
|
||||||
|
- Numerische Bereiche werden mit fuehrenden Nullen auf die urspruengliche Breite expandiert.
|
||||||
|
- Alphanumerische 4-stellige Bereiche mit gleichem 2-stelligem Praefix werden ueber die Zeichenfolge `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ` expandiert.
|
||||||
|
- Der Vergleich fuer die Regelauflosung bleibt CHAR-/String-basiert, passend zu KEDR.
|
||||||
|
- Zeilen mit `DELETE_FLG = X` werden ignoriert.
|
||||||
|
- Bei Ueberlappungen gewinnt die neueste `VALID_FROM`.
|
||||||
|
- Bei gleicher `VALID_FROM` gewinnt der spezifische Einzelwert gegenueber einem Bereich.
|
||||||
|
|
||||||
|
Wichtig: `ZPRODSPARTE_MAP` braucht keine Felder `PAPH1_VON`, `PAPH1_BIS` oder `GUELTAB`. Die Von-bis-Logik wird beim Build auf einzelne PAPH1-Zeilen aufgeloest.
|
||||||
|
|
||||||
|
## ABAP-Objekte
|
||||||
|
|
||||||
|
### `Z_PRODSPARTE_KEDR_K9R_FIND`
|
||||||
|
|
||||||
|
Diagnose-Report zur Ermittlung der relevanten KEDR/K9R-Tabellen.
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- `K9RT761000002` ist die Regel `ProdHierarchie01-1 -> Produktfamilie`.
|
||||||
|
- `K9RT761000003` ist die Regel `Produktfamilie -> Produktsparte`.
|
||||||
|
|
||||||
|
### `Z_PRODSPARTE_MAP_BUILD`
|
||||||
|
|
||||||
|
Finaler Build-Report fuer `ZPRODSPARTE_MAP`.
|
||||||
|
|
||||||
|
Aufgaben:
|
||||||
|
|
||||||
|
- liest `K9RT761000002`
|
||||||
|
- liest `K9RT761000003`
|
||||||
|
- expandiert alle PAPH1-Von-bis-Regeln
|
||||||
|
- nimmt optional zusaetzliche reale PAPH1-Codes aus `MVKE` und `CE11000` auf
|
||||||
|
- loest `PAPH1 -> WWPFA`
|
||||||
|
- loest `WWPFA -> WWPSP`
|
||||||
|
- schreibt die flache Tabelle `ZPRODSPARTE_MAP`
|
||||||
|
|
||||||
|
Parameter:
|
||||||
|
|
||||||
|
- `P_VKORG`: optional, fuer reale Zusatzcodes aus `MVKE`
|
||||||
|
- `P_VTWEG`: optional, fuer reale Zusatzcodes aus `MVKE`
|
||||||
|
- `P_CE`: optional, liest zusaetzliche reale PAPH1 aus `CE11000`
|
||||||
|
- `P_TEST`: Testlauf ohne DB-Schreiben
|
||||||
|
|
||||||
|
Letzter Testlauf:
|
||||||
|
|
||||||
|
- KEDR-Regeln `PAPH1 -> WWPFA`: 124
|
||||||
|
- KEDR-Regeln `WWPFA -> WWPSP`: 81
|
||||||
|
- PAPH1-Codes aus KEDE-Expansion: 1'297
|
||||||
|
- Zusaetzliche reale Code-Versuche: 689
|
||||||
|
- Nicht expandierbare Bereiche: 0
|
||||||
|
- PAPH1-Codes gesamt eindeutig: 1'297
|
||||||
|
- Saetze fuer `ZPRODSPARTE_MAP`: 1'296
|
||||||
|
- PAPH1 ohne Produktfamilie: 1
|
||||||
|
- PAPH1 ohne Produktsparte: 0
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
|
||||||
|
- Die 1'296 Mapping-Saetze entsprechen der Excel/Data(4)-Referenz.
|
||||||
|
- Der eine zusaetzliche PAPH1 ohne Produktfamilie ist die bekannte Luecke `8950`.
|
||||||
|
- `8950` kommt aus realen SAP-Daten, ist aber nicht in KEDE/Excel gepflegt.
|
||||||
|
|
||||||
|
### `Z_PRODSPARTE_MAP_EXPORT`
|
||||||
|
|
||||||
|
Exportiert die flache Tabelle `ZPRODSPARTE_MAP` fuer den Kontrollvergleich gegen Excel/Data(4).
|
||||||
|
|
||||||
|
Exportformat:
|
||||||
|
|
||||||
|
`PAPH1;WWPFA;WWPSP`
|
||||||
|
|
||||||
|
Beispiel-Datei:
|
||||||
|
|
||||||
|
`C:\temp\zprodspartesap3.csv`
|
||||||
|
|
||||||
|
Dieser Export ist der richtige Vergleich gegen Excel/Data(4), nicht der Materialexport.
|
||||||
|
|
||||||
|
### `ZCL_PRODSPARTE_PROVIDER`
|
||||||
|
|
||||||
|
Globale Klasse mit statischer Methode `GET_DATA`.
|
||||||
|
|
||||||
|
Wichtige Signatur:
|
||||||
|
|
||||||
|
- `IV_VKORG TYPE VKORG`
|
||||||
|
- `IV_VTWEG TYPE VTWEG OPTIONAL`
|
||||||
|
- `IV_SPRAS TYPE SPRAS DEFAULT SY-LANGU`
|
||||||
|
- `IV_FALLBACK TYPE BEZEK DEFAULT 'Nicht zugeordnet'`
|
||||||
|
- `VALUE(RT_OUT) TYPE TT_OUT`
|
||||||
|
|
||||||
|
Rueckgabefelder:
|
||||||
|
|
||||||
|
- `MATNR`
|
||||||
|
- `MAKTX`
|
||||||
|
- `PAPH1`
|
||||||
|
- `PAPH1_TEXT`
|
||||||
|
- `WWPFA`
|
||||||
|
- `WWPFA_TEXT`
|
||||||
|
- `WWPSP`
|
||||||
|
- `WWPSP_TEXT`
|
||||||
|
- `IS_ASSIGNED`
|
||||||
|
|
||||||
|
Logik:
|
||||||
|
|
||||||
|
- liest Materialdaten aus `MVKE`
|
||||||
|
- liest Materialtext aus `MAKT`
|
||||||
|
- setzt `PAPH1 = MVKE-PRODH(5)`
|
||||||
|
- liest `ZPRODSPARTE_MAP`
|
||||||
|
- setzt `IS_ASSIGNED = X`, wenn ein Mapping gefunden wurde
|
||||||
|
- setzt sonst Fallback `WWPSP = UNASS`, Text `Nicht zugeordnet`
|
||||||
|
- liest Texte aus `T179T`, `T25A0`, `T25A1`
|
||||||
|
|
||||||
|
Wichtig:
|
||||||
|
|
||||||
|
`GET_DATA` muss in SE24 als `CLASS-METHODS` angelegt sein. Sonst kommt der Fehler:
|
||||||
|
|
||||||
|
`Die Angabe "class=>method" darf nur bei statischen Methoden verwendet werden.`
|
||||||
|
|
||||||
|
### `Z_PRODSPARTE_ALL`
|
||||||
|
|
||||||
|
Ausfuehrbarer Report fuer Materialexport und ALV-Kontrolle.
|
||||||
|
|
||||||
|
Aufgaben:
|
||||||
|
|
||||||
|
- ruft `ZCL_PRODSPARTE_PROVIDER=>GET_DATA`
|
||||||
|
- zeigt optional ALV
|
||||||
|
- exportiert optional tab-getrennte CSV per `cl_gui_frontend_services=>gui_download`
|
||||||
|
|
||||||
|
Dieser Export enthaelt Materialzeilen. Er ist nicht identisch mit der Excel/Data(4)-Mappingreferenz, weil Excel auch PAPH1-Codes ohne aktuelles Material enthalten kann.
|
||||||
|
|
||||||
|
### `PRODUCTDIVISIONR_GET_ENTITYSET`
|
||||||
|
|
||||||
|
OData-GET_ENTITYSET-Methode fuer die Produktspartenreferenz.
|
||||||
|
|
||||||
|
Aufgaben:
|
||||||
|
|
||||||
|
- verwendet ohne Filter die Default-TR-AG-Verkaufsorganisation `VKORG = 1100`
|
||||||
|
- liest optional `VKORG`, falls die Entity spaeter um dieses Property erweitert wird
|
||||||
|
- liest optional `VTWEG`
|
||||||
|
- liest optional `SPRAS`
|
||||||
|
- ruft `ZCL_PRODSPARTE_PROVIDER=>GET_DATA`
|
||||||
|
- gibt die Daten per `CORRESPONDING` an das EntitySet zurueck
|
||||||
|
|
||||||
|
Wichtig: Die aktuelle Gateway-Metadata fuer `ProductDivisionRef` enthaelt kein Property `VKORG`.
|
||||||
|
Darum darf die DPC_EXT-Methode `VKORG` nicht als OData-Pflichtfilter erzwingen. Sonst kann die App
|
||||||
|
`ProductDivisionRefSet` nicht laden: ohne Filter kommt `Filter VKORG ist erforderlich`, mit Filter
|
||||||
|
kommt `Property VKORG not found in type ProductDivisionRef`.
|
||||||
|
|
||||||
|
### `Z_PRODSPARTE_MAP_IMPORT`
|
||||||
|
|
||||||
|
Nur noch Fallback/Hilfsprogramm.
|
||||||
|
|
||||||
|
Es kann CSV/Excel-Daten in die Mapping-Tabelle bringen, ist aber fuer die finale Architektur nicht die fuehrende Loesung. Fuehrend ist KEDE/KEDR ueber `Z_PRODSPARTE_MAP_BUILD`.
|
||||||
|
|
||||||
|
## Validierung
|
||||||
|
|
||||||
|
### Materialexport nach KEDR-Build
|
||||||
|
|
||||||
|
Datei:
|
||||||
|
|
||||||
|
`prodspartesap2.csv`
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- Materialzeilen: 42'232
|
||||||
|
- Zugeordnet: 42'230
|
||||||
|
- Nicht zugeordnet: 2
|
||||||
|
- Nicht zugeordneter PAPH1: `8950`
|
||||||
|
- WWPFA-Abweichungen gegen Excel fuer abgedeckte Codes: 0
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
|
||||||
|
Die Materialdaten sind bis auf die fachlich ungepflegte Luecke `8950` zugeordnet.
|
||||||
|
|
||||||
|
### Mappingexport gegen Excel/Data(4)
|
||||||
|
|
||||||
|
Dateien:
|
||||||
|
|
||||||
|
- Excel-Referenz: `exceldataexport.csv`
|
||||||
|
- SAP-Mappingexport: `C:\temp\zprodspartesap3.csv`
|
||||||
|
|
||||||
|
Vergleichsergebnis:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Excel-Rohzeilen: 897
|
||||||
|
Excel expandiert auf PAPH1: 1296
|
||||||
|
SAP-Mapping-Zeilen: 1296
|
||||||
|
Fehlt in SAP: 0
|
||||||
|
Zusätzlich in SAP: 0
|
||||||
|
WWPFA-Abweichungen: 0
|
||||||
|
Duplikate PAPH1: 0
|
||||||
|
Leere WWPSP: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Fazit:
|
||||||
|
|
||||||
|
`ZPRODSPARTE_MAP` entspricht der Excel/Data(4)-Referenz vollstaendig fuer `PAPH1 -> WWPFA`. `WWPSP` ist zusaetzlich aus der zweiten KEDE-Regel `K9RT761000003` gefuellt.
|
||||||
|
|
||||||
|
## Offene fachliche Punkte
|
||||||
|
|
||||||
|
### OData-Import `ProductDivisionRefSet`
|
||||||
|
|
||||||
|
Am 2026-06-10 wurde der echte App-Import fuer `ZSCHWEIZ` getestet.
|
||||||
|
|
||||||
|
Konfiguration in der App-DB:
|
||||||
|
|
||||||
|
- Site-ID: `9`
|
||||||
|
- TSC: `ZSCHWEIZ`
|
||||||
|
- SAP-Service: `ZPOWERBI_EINKAUF_SRV`
|
||||||
|
- aktive Quelle `Z`: `FinanzdataSchweizOeSet`
|
||||||
|
- aktive Quelle `P`: `ProductDivisionRefSet`
|
||||||
|
- aktiver Join: `Z.Matnr = P.Matnr`
|
||||||
|
- Spartenfelder werden aus `P.Paph1`, `P.Wwpfa`, `P.Wwpsp`, `P.IsAssigned` gemappt
|
||||||
|
|
||||||
|
Gefundener Fehler vor ABAP-Korrektur:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ProductDivisionRefSet?$format=json
|
||||||
|
HTTP 400
|
||||||
|
Filter VKORG ist erforderlich
|
||||||
|
|
||||||
|
ProductDivisionRefSet?$format=json&$filter=VKORG eq '1100'
|
||||||
|
HTTP 400
|
||||||
|
Property VKORG not found in type ProductDivisionRef
|
||||||
|
```
|
||||||
|
|
||||||
|
Metadata `ProductDivisionRef`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Matnr
|
||||||
|
Maktx
|
||||||
|
Paph1
|
||||||
|
Paph1Text
|
||||||
|
Wwpfa
|
||||||
|
WwpfaText
|
||||||
|
Wwpsp
|
||||||
|
WwpspText
|
||||||
|
IsAssigned
|
||||||
|
```
|
||||||
|
|
||||||
|
Korrektur:
|
||||||
|
|
||||||
|
- `ZCL_PRODSPARTE_DPC_EXT_PRODUCTDIVISIONR_GET_ENTITYSET.abap` setzt `lv_vkorg = '1100'` als Default.
|
||||||
|
- Die Exception `Filter VKORG ist erforderlich` wurde entfernt.
|
||||||
|
- Damit kann die bestehende App `ProductDivisionRefSet` ohne Filter laden.
|
||||||
|
|
||||||
|
Nach Aktivierung dieser ABAP-Aenderung in SAP muss der `ZSCHWEIZ`-Import erneut laufen.
|
||||||
|
|
||||||
|
Status nach Aktivierung `VKORG = 1100`:
|
||||||
|
|
||||||
|
- `ProductDivisionRefSet` liefert 42'232 Zeilen.
|
||||||
|
- Beispielzeilen sind korrekt gefuellt, z.B. `Matnr=6`, `Paph1=0414`, `WWPFA=0004`, `WWPSP=0001`, `IsAssigned=True`.
|
||||||
|
- Der gezielte App-Import fuer `ZSCHWEIZ` lief erfolgreich durch.
|
||||||
|
- Importierte CH/AT-Zeilen: 40'292
|
||||||
|
- Davon mit Spartenreferenz zugeordnet: 36'847
|
||||||
|
- `Nicht zugeordnet` mit vorhandener Referenz/`UNASS`: 0
|
||||||
|
- Ohne Treffer in `ProductDivisionRefSet`: 3'445
|
||||||
|
|
||||||
|
Aufteilung nach TSC:
|
||||||
|
|
||||||
|
```text
|
||||||
|
TSC Rows Assigned Nicht zugeordnet Kein Referenztreffer
|
||||||
|
TRCH 38'838 35'524 0 3'314
|
||||||
|
TRAT 1'454 1'323 0 131
|
||||||
|
```
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
|
||||||
|
- Die eigentliche Spartenlogik im Webservice ist fuer CH/AT jetzt sauber: kein `UNASS`/`Nicht zugeordnet`.
|
||||||
|
- Die verbleibenden Zeilen sind kein Spartenregelproblem, sondern Materialnummern ohne Treffer in der TR-AG-Referenz.
|
||||||
|
|
||||||
|
## Dashboard-Fallback fuer fehlende Materialreferenzen
|
||||||
|
|
||||||
|
Es gibt Verkaufszeilen, bei denen der materialbasierte OData-Service `ProductDivisionRefSet` keinen Treffer liefert. Ein Teil dieser Zeilen hat aber in der Verkaufsquelle noch eine Produktgruppe/Produkthierarchie (`Z.Prodh`). Deshalb wurde ein zweiter, flacher OData-Service vorgesehen:
|
||||||
|
|
||||||
|
- `ProductDivisionRefSet`: Materialnummer `MATNR` -> `PAPH1/WWPFA/WWPSP`
|
||||||
|
- `ProductDivisionMapSet`: Produkthierarchie `PAPH1` -> `WWPFA/WWPSP`
|
||||||
|
|
||||||
|
Der neue ABAP-GET_ENTITYSET fuer `ProductDivisionMapSet` liest direkt aus `ZPRODSPARTE_MAP` und liefert eine Zeile pro `PAPH1`. Im Dashboard wird dieser Service als Quelle `M` angebunden und per Left Join auf die Verkaufsdaten gelegt:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Z.Prodh = M.Paph1
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Mapping-Felder verwenden danach `FirstNonEmpty(P.*, M.*)`. Das bedeutet:
|
||||||
|
|
||||||
|
- Wenn die materialbasierte Referenz `P` einen Treffer liefert, gewinnt `P`.
|
||||||
|
- Wenn `P` leer ist, aber `Z.Prodh` in `M` gefunden wird, werden Familie und Sparte aus der flachen Mapping-Tabelle genommen.
|
||||||
|
- Wenn auch `Z.Prodh` leer ist oder nicht in `ZPRODSPARTE_MAP` steht, bleibt die Zeile ohne Produktsparten-Zuordnung.
|
||||||
|
|
||||||
|
Aktueller CH/AT-Befund vor diesem Fallback:
|
||||||
|
|
||||||
|
- 3'445 Verkaufszeilen hatten keinen Materialreferenztreffer.
|
||||||
|
- 106 davon hatten `Z.Prodh = 9999`.
|
||||||
|
- `9999` ist in Excel/Data(4) und in `ZPRODSPARTE_MAP` vorhanden: `9999 -> 0043 -> 0008`.
|
||||||
|
- Diese 106 Zeilen sollten durch `ProductDivisionMapSet` zuordenbar werden.
|
||||||
|
- Die restlichen Zeilen mit leerer Produktgruppe koennen technisch nicht zugeordnet werden, solange SAP keine Produktgruppe/Materialreferenz liefert.
|
||||||
|
|
||||||
|
Verifizierter Stand nach Aktivierung von `ProductDivisionMapSet`:
|
||||||
|
|
||||||
|
- `$metadata` enthaelt `ProductDivisionMap` mit `WwpfaText` korrekt geschrieben.
|
||||||
|
- `ProductDivisionMapSet` liefert HTTP 200 und 1'296 Zeilen.
|
||||||
|
- Lokaler CH/AT-Import verwendet Quellen `Z`, `P`, `M` und zwei Left Joins.
|
||||||
|
- Ergebnis CH/AT gesamt: 40'292 Verkaufszeilen, 36'953 zugeordnet, 0 `UnassignedWithReference`.
|
||||||
|
- Gegenueber dem vorherigen Stand sind genau 106 zusaetzliche Zeilen zugeordnet.
|
||||||
|
- Rest ohne Referenz: TRCH 3'312, TRAT 27; bei diesen Zeilen ist `ProductGroup/Z.Prodh` leer.
|
||||||
|
|
||||||
|
### `PAPH1 = 8950`
|
||||||
|
|
||||||
|
`8950` taucht in echten SAP-Daten auf, ist aber nicht in KEDE/Excel gepflegt.
|
||||||
|
|
||||||
|
Auswirkung:
|
||||||
|
|
||||||
|
- Im Mappingexport fehlt `8950` korrekt, weil Excel/KEDE es auch nicht enthaelt.
|
||||||
|
- Im Materialexport bleibt `8950` als nicht zugeordnet.
|
||||||
|
|
||||||
|
Moegliche Ursachen:
|
||||||
|
|
||||||
|
- Material-PRODH ist falsch oder veraltet.
|
||||||
|
- Finance hat fuer `8950` noch keine KEDE-Regel gepflegt.
|
||||||
|
- Der Code soll fachlich bewusst nicht zugeordnet sein.
|
||||||
|
|
||||||
|
Naechster Schritt:
|
||||||
|
|
||||||
|
Finance/SAP-Verantwortliche muessen entscheiden, ob fuer `8950` eine KEDE-Regel gepflegt wird oder ob der Materialstamm korrigiert wird.
|
||||||
|
|
||||||
|
## Bedienablauf fuer naechste Pruefung
|
||||||
|
|
||||||
|
1. `Z_PRODSPARTE_MAP_BUILD` mit `P_TEST = X` laufen lassen.
|
||||||
|
2. Pruefen:
|
||||||
|
- `Nicht expandierbare Bereiche = 0`
|
||||||
|
- `PAPH1 ohne Produktsparte = 0`
|
||||||
|
- `PAPH1 ohne Produktfamilie` nur bekannte Luecken, aktuell `8950`
|
||||||
|
3. `Z_PRODSPARTE_MAP_BUILD` ohne `P_TEST` laufen lassen.
|
||||||
|
4. `Z_PRODSPARTE_MAP_EXPORT` laufen lassen.
|
||||||
|
5. Export gegen Excel/Data(4) vergleichen.
|
||||||
|
6. `Z_PRODSPARTE_ALL` laufen lassen, um Materialzuordnung zu pruefen.
|
||||||
|
7. OData-Service testen.
|
||||||
|
|
||||||
|
## Wichtige Unterscheidung der CSV-Dateien
|
||||||
|
|
||||||
|
`prodspartesap2.csv` oder Export aus `Z_PRODSPARTE_ALL`:
|
||||||
|
|
||||||
|
- Materialexport
|
||||||
|
- eine Zeile pro Material
|
||||||
|
- darf weniger PAPH1-Codes enthalten als Excel, weil nicht jeder Referenzcode ein Material haben muss
|
||||||
|
- dient zur Dashboard-/Materialpruefung
|
||||||
|
|
||||||
|
`zprodspartesap3.csv` oder Export aus `Z_PRODSPARTE_MAP_EXPORT`:
|
||||||
|
|
||||||
|
- Mappingexport
|
||||||
|
- eine Zeile pro PAPH1
|
||||||
|
- muss gegen Excel/Data(4) identisch sein bei `PAPH1 -> WWPFA`
|
||||||
|
- ist die richtige Datei fuer den Regelabgleich
|
||||||
|
|
||||||
|
## Bekannte technische Stolperstellen
|
||||||
|
|
||||||
|
- `GET_DATA` muss statisch sein, wenn mit `zcl_prodsparte_provider=>get_data` aufgerufen wird.
|
||||||
|
- `TEXT-001` im Report `Z_PRODSPARTE_ALL` muss als Textsymbol existieren.
|
||||||
|
- CSV-Downloads mit `cl_gui_frontend_services=>gui_download` funktionieren nur im SAP GUI.
|
||||||
|
- `CORRESPONDING #( lt_data )` im OData-Code funktioniert nur bei passenden Feld-/Property-Namen.
|
||||||
|
- `PAPH1 = MVKE-PRODH(5)` ist aktuell korrekt, weil KEDE `SOUR1_FROM` in `K9RT761000002` CHAR5 ist.
|
||||||
|
- `T179T-PRODH` ist CHAR18; falls `PAPH1_TEXT` leer bleibt, muss der Text-Key linksbuendig/padding-geprueft werden.
|
||||||
|
- `DELETE ADJACENT DUPLICATES COMPARING matnr` im Provider nimmt bei mehreren Vertriebswegen den ersten sortierten Satz.
|
||||||
|
|
||||||
|
## Dateien im Ordner `spartenlogic`
|
||||||
|
|
||||||
|
- `Z_PRODSPARTE_KEDR_K9R_FIND.abap`: Diagnose der TKEDRS/K9R-Regeltabellen
|
||||||
|
- `Z_PRODSPARTE_MAP_BUILD.abap`: finaler Build aus KEDE/KEDR nach `ZPRODSPARTE_MAP`
|
||||||
|
- `Z_PRODSPARTE_MAP_EXPORT.abap`: Export der flachen Mapping-Tabelle
|
||||||
|
- `ZCL_PRODSPARTE_PROVIDER.abap`: Provider fuer Materialdaten und Mapping-Lookup
|
||||||
|
- `Z_PRODSPARTE_ALL.abap`: ALV/CSV-Materialexport
|
||||||
|
- `ZCL_PRODSPARTE_DPC_EXT_PRODUCTDIVISIONR_GET_ENTITYSET.abap`: OData-GET_ENTITYSET-Methode
|
||||||
|
- `ZCL_PRODSPARTE_DPC_EXT_PRODUCTDIVISIONM_GET_ENTITYSET.abap`: OData-GET_ENTITYSET fuer die flache PAPH1-Mappingquelle `ProductDivisionMapSet`
|
||||||
|
- `Z_PRODSPARTE_MAP_IMPORT.abap`: Fallback-Import, nicht fuehrend
|
||||||
|
- `exceldataexport.csv`: Excel/Data(4)-Referenzexport
|
||||||
|
- `prodspartesap2.csv`: Materialexport nach KEDR-Build
|
||||||
|
- `markregell.png`: Screenshot der KEDE-von/bis-Regel
|
||||||
|
- `ruleresult.txt`: Diagnoseergebnis aus SAP
|
||||||
|
|
||||||
|
## Fazit
|
||||||
|
|
||||||
|
Die Produktspartenlogik ist fachlich und technisch final auf KEDE/KEDR als Quelle ausgerichtet. Die flache Tabelle `ZPRODSPARTE_MAP` wird aus den SAP-Regeln aufgebaut und wurde gegen Excel/Data(4) ohne Abweichung validiert. Der Dashboard-/OData-Pfad nutzt diese Tabelle als Lookup: zuerst materialbasiert ueber `ProductDivisionRefSet`, danach als Fallback ueber `ProductDivisionMapSet` anhand `Z.Prodh`. Die einzige bekannte fachliche Luecke ist `PAPH1 = 8950`; Zeilen ohne Produktgruppe bleiben mangels Schluessel nicht zuordenbar.
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
METHOD productdivisionm_get_entityset.
|
||||||
|
TYPES: BEGIN OF ty_out,
|
||||||
|
paph1 TYPE zprodsparte_map-paph1,
|
||||||
|
paph1_text TYPE t179t-vtext,
|
||||||
|
wwpfa TYPE zprodsparte_map-wwpfa,
|
||||||
|
wwpfa_text TYPE t25a0-bezek,
|
||||||
|
wwpsp TYPE zprodsparte_map-wwpsp,
|
||||||
|
wwpsp_text TYPE t25a1-bezek,
|
||||||
|
is_assigned TYPE abap_bool,
|
||||||
|
END OF ty_out.
|
||||||
|
|
||||||
|
DATA: lv_spras TYPE spras.
|
||||||
|
lv_spras = sy-langu.
|
||||||
|
|
||||||
|
LOOP AT it_filter_select_options INTO DATA(ls_filter).
|
||||||
|
READ TABLE ls_filter-select_options INTO DATA(ls_so) INDEX 1.
|
||||||
|
IF sy-subrc <> 0.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DATA(lv_property) = ls_filter-property.
|
||||||
|
TRANSLATE lv_property TO UPPER CASE.
|
||||||
|
|
||||||
|
CASE lv_property.
|
||||||
|
WHEN 'SPRAS'.
|
||||||
|
lv_spras = ls_so-low.
|
||||||
|
ENDCASE.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
SELECT paph1, wwpfa, wwpsp
|
||||||
|
FROM zprodsparte_map
|
||||||
|
INTO TABLE @DATA(lt_map)
|
||||||
|
WHERE paph1 <> @space.
|
||||||
|
|
||||||
|
IF lt_map IS INITIAL.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
SORT lt_map BY paph1.
|
||||||
|
DELETE ADJACENT DUPLICATES FROM lt_map COMPARING paph1.
|
||||||
|
|
||||||
|
SELECT prodh, vtext
|
||||||
|
FROM t179t
|
||||||
|
INTO TABLE @DATA(lt_h)
|
||||||
|
WHERE spras = @lv_spras.
|
||||||
|
SORT lt_h BY prodh.
|
||||||
|
|
||||||
|
SELECT wwpfa, bezek
|
||||||
|
FROM t25a0
|
||||||
|
INTO TABLE @DATA(lt_fam)
|
||||||
|
WHERE spras = @lv_spras.
|
||||||
|
SORT lt_fam BY wwpfa.
|
||||||
|
|
||||||
|
SELECT wwpsp, bezek
|
||||||
|
FROM t25a1
|
||||||
|
INTO TABLE @DATA(lt_spa)
|
||||||
|
WHERE spras = @lv_spras.
|
||||||
|
SORT lt_spa BY wwpsp.
|
||||||
|
|
||||||
|
DATA lt_out TYPE STANDARD TABLE OF ty_out WITH DEFAULT KEY.
|
||||||
|
|
||||||
|
LOOP AT lt_map INTO DATA(ls_map).
|
||||||
|
DATA(ls_out) = VALUE ty_out(
|
||||||
|
paph1 = ls_map-paph1
|
||||||
|
wwpfa = ls_map-wwpfa
|
||||||
|
wwpsp = ls_map-wwpsp
|
||||||
|
is_assigned = abap_true ).
|
||||||
|
|
||||||
|
READ TABLE lt_h INTO DATA(ls_h)
|
||||||
|
WITH KEY prodh = ls_map-paph1 BINARY SEARCH.
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
ls_out-paph1_text = ls_h-vtext.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
READ TABLE lt_fam INTO DATA(ls_f)
|
||||||
|
WITH KEY wwpfa = ls_map-wwpfa BINARY SEARCH.
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
ls_out-wwpfa_text = ls_f-bezek.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
READ TABLE lt_spa INTO DATA(ls_s)
|
||||||
|
WITH KEY wwpsp = ls_map-wwpsp BINARY SEARCH.
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
ls_out-wwpsp_text = ls_s-bezek.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
APPEND ls_out TO lt_out.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
et_entityset = CORRESPONDING #( lt_out ).
|
||||||
|
ENDMETHOD.
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
METHOD productdivisionr_get_entityset.
|
||||||
|
DATA: lv_vkorg TYPE vkorg,
|
||||||
|
lv_vtweg TYPE vtweg,
|
||||||
|
lv_spras TYPE spras.
|
||||||
|
|
||||||
|
* ProductDivisionRef enthaelt laut Gateway-Metadata kein VKORG-Feld.
|
||||||
|
* Der Dashboard-Import kann deshalb keinen VKORG-Filter senden.
|
||||||
|
* Default ist die fuehrende TR-AG-Verkaufsorganisation.
|
||||||
|
lv_vkorg = '1100'.
|
||||||
|
lv_spras = sy-langu.
|
||||||
|
|
||||||
|
LOOP AT it_filter_select_options INTO DATA(ls_filter).
|
||||||
|
READ TABLE ls_filter-select_options INTO DATA(ls_so) INDEX 1.
|
||||||
|
IF sy-subrc <> 0.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DATA(lv_property) = ls_filter-property.
|
||||||
|
TRANSLATE lv_property TO UPPER CASE.
|
||||||
|
|
||||||
|
CASE lv_property.
|
||||||
|
WHEN 'VKORG'.
|
||||||
|
lv_vkorg = ls_so-low.
|
||||||
|
WHEN 'VTWEG'.
|
||||||
|
lv_vtweg = ls_so-low.
|
||||||
|
WHEN 'SPRAS'.
|
||||||
|
lv_spras = ls_so-low.
|
||||||
|
ENDCASE.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
DATA(lt_data) = zcl_prodsparte_provider=>get_data(
|
||||||
|
iv_vkorg = lv_vkorg
|
||||||
|
iv_vtweg = lv_vtweg
|
||||||
|
iv_spras = lv_spras ).
|
||||||
|
|
||||||
|
et_entityset = CORRESPONDING #( lt_data ).
|
||||||
|
ENDMETHOD.
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
CLASS-POOL zcl_prodsparte_provider.
|
||||||
|
|
||||||
|
CLASS zcl_prodsparte_provider DEFINITION
|
||||||
|
PUBLIC
|
||||||
|
FINAL
|
||||||
|
CREATE PUBLIC.
|
||||||
|
|
||||||
|
PUBLIC SECTION.
|
||||||
|
TYPES: BEGIN OF ty_out,
|
||||||
|
matnr TYPE mvke-matnr,
|
||||||
|
maktx TYPE makt-maktx,
|
||||||
|
paph1 TYPE ce11000-paph1,
|
||||||
|
paph1_text TYPE t179t-vtext,
|
||||||
|
wwpfa TYPE ce11000-wwpfa,
|
||||||
|
wwpfa_text TYPE t25a0-bezek,
|
||||||
|
wwpsp TYPE ce11000-wwpsp,
|
||||||
|
wwpsp_text TYPE t25a1-bezek,
|
||||||
|
is_assigned TYPE abap_bool,
|
||||||
|
END OF ty_out.
|
||||||
|
TYPES tt_out TYPE STANDARD TABLE OF ty_out WITH DEFAULT KEY.
|
||||||
|
|
||||||
|
CLASS-METHODS get_data
|
||||||
|
IMPORTING
|
||||||
|
iv_vkorg TYPE vkorg
|
||||||
|
iv_vtweg TYPE vtweg OPTIONAL
|
||||||
|
iv_spras TYPE spras DEFAULT sy-langu
|
||||||
|
iv_fallback TYPE t25a1-bezek DEFAULT 'Nicht zugeordnet'
|
||||||
|
RETURNING
|
||||||
|
VALUE(rt_out) TYPE tt_out.
|
||||||
|
ENDCLASS.
|
||||||
|
|
||||||
|
CLASS zcl_prodsparte_provider IMPLEMENTATION.
|
||||||
|
METHOD get_data.
|
||||||
|
TYPES: BEGIN OF ty_base,
|
||||||
|
matnr TYPE mvke-matnr,
|
||||||
|
vtweg TYPE mvke-vtweg,
|
||||||
|
prodh TYPE mvke-prodh,
|
||||||
|
paph1 TYPE ce11000-paph1,
|
||||||
|
maktx TYPE makt-maktx,
|
||||||
|
END OF ty_base.
|
||||||
|
|
||||||
|
TYPES: BEGIN OF ty_map,
|
||||||
|
paph1 TYPE zprodsparte_map-paph1,
|
||||||
|
wwpfa TYPE zprodsparte_map-wwpfa,
|
||||||
|
wwpsp TYPE zprodsparte_map-wwpsp,
|
||||||
|
END OF ty_map.
|
||||||
|
|
||||||
|
DATA: lt_base TYPE STANDARD TABLE OF ty_base WITH DEFAULT KEY,
|
||||||
|
lt_map TYPE STANDARD TABLE OF ty_map WITH DEFAULT KEY.
|
||||||
|
|
||||||
|
IF iv_vtweg IS INITIAL.
|
||||||
|
SELECT mvke~matnr,
|
||||||
|
mvke~vtweg,
|
||||||
|
mvke~prodh,
|
||||||
|
makt~maktx
|
||||||
|
FROM mvke
|
||||||
|
LEFT OUTER JOIN makt
|
||||||
|
ON makt~matnr = mvke~matnr
|
||||||
|
AND makt~spras = @iv_spras
|
||||||
|
INTO CORRESPONDING FIELDS OF TABLE @lt_base
|
||||||
|
WHERE mvke~vkorg = @iv_vkorg
|
||||||
|
AND mvke~prodh <> @space.
|
||||||
|
ELSE.
|
||||||
|
SELECT mvke~matnr,
|
||||||
|
mvke~vtweg,
|
||||||
|
mvke~prodh,
|
||||||
|
makt~maktx
|
||||||
|
FROM mvke
|
||||||
|
LEFT OUTER JOIN makt
|
||||||
|
ON makt~matnr = mvke~matnr
|
||||||
|
AND makt~spras = @iv_spras
|
||||||
|
INTO CORRESPONDING FIELDS OF TABLE @lt_base
|
||||||
|
WHERE mvke~vkorg = @iv_vkorg
|
||||||
|
AND mvke~vtweg = @iv_vtweg
|
||||||
|
AND mvke~prodh <> @space.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF lt_base IS INITIAL.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
LOOP AT lt_base ASSIGNING FIELD-SYMBOL(<ls_base>).
|
||||||
|
<ls_base>-paph1 = <ls_base>-prodh(5).
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
SORT lt_base BY matnr vtweg.
|
||||||
|
DELETE ADJACENT DUPLICATES FROM lt_base COMPARING matnr.
|
||||||
|
|
||||||
|
SELECT paph1, wwpfa, wwpsp
|
||||||
|
FROM zprodsparte_map
|
||||||
|
INTO TABLE @lt_map
|
||||||
|
WHERE wwpfa <> @space.
|
||||||
|
SORT lt_map BY paph1.
|
||||||
|
|
||||||
|
SELECT prodh, vtext
|
||||||
|
FROM t179t
|
||||||
|
INTO TABLE @DATA(lt_h)
|
||||||
|
WHERE spras = @iv_spras.
|
||||||
|
SORT lt_h BY prodh.
|
||||||
|
|
||||||
|
SELECT wwpfa, bezek
|
||||||
|
FROM t25a0
|
||||||
|
INTO TABLE @DATA(lt_fam)
|
||||||
|
WHERE spras = @iv_spras.
|
||||||
|
SORT lt_fam BY wwpfa.
|
||||||
|
|
||||||
|
SELECT wwpsp, bezek
|
||||||
|
FROM t25a1
|
||||||
|
INTO TABLE @DATA(lt_spa)
|
||||||
|
WHERE spras = @iv_spras.
|
||||||
|
SORT lt_spa BY wwpsp.
|
||||||
|
|
||||||
|
LOOP AT lt_base INTO DATA(ls_base).
|
||||||
|
DATA(ls_out) = VALUE ty_out(
|
||||||
|
matnr = ls_base-matnr
|
||||||
|
maktx = ls_base-maktx
|
||||||
|
paph1 = ls_base-paph1
|
||||||
|
wwpsp = 'UNASS'
|
||||||
|
wwpsp_text = iv_fallback
|
||||||
|
is_assigned = abap_false ).
|
||||||
|
|
||||||
|
READ TABLE lt_h INTO DATA(ls_h)
|
||||||
|
WITH KEY prodh = ls_base-prodh BINARY SEARCH.
|
||||||
|
IF sy-subrc <> 0.
|
||||||
|
DATA(lv_prodh_key) = VALUE t179t-prodh( ).
|
||||||
|
lv_prodh_key = ls_out-paph1.
|
||||||
|
READ TABLE lt_h INTO ls_h
|
||||||
|
WITH KEY prodh = lv_prodh_key BINARY SEARCH.
|
||||||
|
ENDIF.
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
ls_out-paph1_text = ls_h-vtext.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
READ TABLE lt_map INTO DATA(ls_map)
|
||||||
|
WITH KEY paph1 = ls_out-paph1 BINARY SEARCH.
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
ls_out-wwpfa = ls_map-wwpfa.
|
||||||
|
ls_out-wwpsp = ls_map-wwpsp.
|
||||||
|
ls_out-is_assigned = abap_true.
|
||||||
|
|
||||||
|
READ TABLE lt_fam INTO DATA(ls_f)
|
||||||
|
WITH KEY wwpfa = ls_map-wwpfa BINARY SEARCH.
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
ls_out-wwpfa_text = ls_f-bezek.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
READ TABLE lt_spa INTO DATA(ls_s)
|
||||||
|
WITH KEY wwpsp = ls_map-wwpsp BINARY SEARCH.
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
ls_out-wwpsp_text = ls_s-bezek.
|
||||||
|
ENDIF.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
APPEND ls_out TO rt_out.
|
||||||
|
ENDLOOP.
|
||||||
|
ENDMETHOD.
|
||||||
|
ENDCLASS.
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
*& Report Z_PRODSPARTE_ALL
|
||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
*& Zweck: Einziger ausfuehrbarer Report rund um die Provider-Logik.
|
||||||
|
*& Ruft ZCL_PRODSPARTE_PROVIDER=>GET_DATA und kann das Ergebnis
|
||||||
|
*& als ALV anzeigen oder als tab-getrennte CSV exportieren.
|
||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
REPORT z_prodsparte_all.
|
||||||
|
|
||||||
|
PARAMETERS: p_vkorg TYPE vkorg OBLIGATORY.
|
||||||
|
PARAMETERS: p_vtweg TYPE vtweg.
|
||||||
|
PARAMETERS: p_spras TYPE spras DEFAULT sy-langu.
|
||||||
|
PARAMETERS: p_fallb TYPE t25a1-bezek DEFAULT 'Nicht zugeordnet'.
|
||||||
|
|
||||||
|
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME.
|
||||||
|
PARAMETERS: p_alv TYPE abap_bool DEFAULT 'X' AS CHECKBOX.
|
||||||
|
PARAMETERS: p_csv TYPE abap_bool AS CHECKBOX.
|
||||||
|
PARAMETERS: p_file TYPE string LOWER CASE
|
||||||
|
DEFAULT 'C:\temp\prodsparte_export.csv'.
|
||||||
|
SELECTION-SCREEN END OF BLOCK b1.
|
||||||
|
|
||||||
|
START-OF-SELECTION.
|
||||||
|
|
||||||
|
DATA(lt_data) = zcl_prodsparte_provider=>get_data(
|
||||||
|
iv_vkorg = p_vkorg
|
||||||
|
iv_vtweg = p_vtweg
|
||||||
|
iv_spras = p_spras
|
||||||
|
iv_fallback = p_fallb ).
|
||||||
|
|
||||||
|
IF lt_data IS INITIAL.
|
||||||
|
MESSAGE 'Keine Daten - VKORG/VTWEG pruefen.' TYPE 'I'.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
WRITE: / 'Gelesene Saetze:', lines( lt_data ).
|
||||||
|
|
||||||
|
IF p_csv = abap_true.
|
||||||
|
PERFORM export_csv USING lt_data p_file.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF p_alv = abap_true.
|
||||||
|
PERFORM show_alv USING lt_data.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
FORM export_csv USING it_data TYPE zcl_prodsparte_provider=>tt_out
|
||||||
|
iv_file TYPE string.
|
||||||
|
|
||||||
|
DATA: lt_csv TYPE STANDARD TABLE OF string,
|
||||||
|
lv_sep TYPE c LENGTH 1.
|
||||||
|
|
||||||
|
lv_sep = cl_abap_char_utilities=>horizontal_tab.
|
||||||
|
|
||||||
|
APPEND |MATNR{ lv_sep }PAPH1{ lv_sep }PAPH1_TEXT{ lv_sep }WWPFA{ lv_sep }|
|
||||||
|
&& |WWPFA_TEXT{ lv_sep }WWPSP{ lv_sep }WWPSP_TEXT{ lv_sep }|
|
||||||
|
&& |IS_ASSIGNED{ lv_sep }MAKTX| TO lt_csv.
|
||||||
|
|
||||||
|
LOOP AT it_data INTO DATA(ls).
|
||||||
|
APPEND |{ ls-matnr }{ lv_sep }{ ls-paph1 }{ lv_sep }{ ls-paph1_text }|
|
||||||
|
&& |{ lv_sep }{ ls-wwpfa }{ lv_sep }{ ls-wwpfa_text }|
|
||||||
|
&& |{ lv_sep }{ ls-wwpsp }{ lv_sep }{ ls-wwpsp_text }|
|
||||||
|
&& |{ lv_sep }{ ls-is_assigned }{ lv_sep }{ ls-maktx }|
|
||||||
|
TO lt_csv.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
cl_gui_frontend_services=>gui_download(
|
||||||
|
EXPORTING
|
||||||
|
filename = iv_file
|
||||||
|
filetype = 'ASC'
|
||||||
|
CHANGING
|
||||||
|
data_tab = lt_csv
|
||||||
|
EXCEPTIONS
|
||||||
|
OTHERS = 1 ).
|
||||||
|
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
WRITE: / lines( it_data ), 'Saetze exportiert nach', iv_file.
|
||||||
|
ELSE.
|
||||||
|
WRITE: / 'Download-Fehler, sy-subrc=', sy-subrc.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
|
|
||||||
|
FORM show_alv USING it_data TYPE zcl_prodsparte_provider=>tt_out.
|
||||||
|
|
||||||
|
DATA lt_alv TYPE zcl_prodsparte_provider=>tt_out.
|
||||||
|
|
||||||
|
lt_alv = it_data.
|
||||||
|
|
||||||
|
cl_salv_table=>factory(
|
||||||
|
IMPORTING
|
||||||
|
r_salv_table = DATA(lo_alv)
|
||||||
|
CHANGING
|
||||||
|
t_table = lt_alv ).
|
||||||
|
|
||||||
|
lo_alv->get_functions( )->set_all( abap_true ).
|
||||||
|
lo_alv->get_columns( )->set_optimize( abap_true ).
|
||||||
|
lo_alv->display( ).
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
*& Report Z_PRODSPARTE_KEDR_K9R_FIND
|
||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
*& Zweck: Technische KEDE/KEDR-Ableitungsregel finden.
|
||||||
|
*&
|
||||||
|
*& Hintergrund:
|
||||||
|
*& Ableitungsregeln werden in generierten K9R*-Tabellen gespeichert.
|
||||||
|
*& Die Feldnamen koennen generiert sein und muessen nicht PAPH1/WWPFA
|
||||||
|
*& heissen. Deshalb sucht dieser Report:
|
||||||
|
*& 1) TKEDRS-Zeilen mit K9R-/Produkt-/PAPH-/WWPFA-Hinweisen
|
||||||
|
*& 2) alle DDIC-Tabellen K9R* mit Feldliste und Beispielwerten
|
||||||
|
*&
|
||||||
|
*& Bitte die Ausgabe der passenden K9R-Tabelle + Feldliste schicken.
|
||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
REPORT z_prodsparte_kedr_k9r_find.
|
||||||
|
|
||||||
|
PARAMETERS: p_appl TYPE c LENGTH 2 DEFAULT 'KE',
|
||||||
|
p_like TYPE dd02l-tabname DEFAULT 'K9R%',
|
||||||
|
p_rows TYPE i DEFAULT 5.
|
||||||
|
PARAMETERS: p_all TYPE abap_bool AS CHECKBOX.
|
||||||
|
|
||||||
|
FIELD-SYMBOLS: <ls_any> TYPE any,
|
||||||
|
<lt_any> TYPE STANDARD TABLE,
|
||||||
|
<lv_any> TYPE any.
|
||||||
|
|
||||||
|
START-OF-SELECTION.
|
||||||
|
|
||||||
|
PERFORM show_tkedrs.
|
||||||
|
SKIP 2.
|
||||||
|
PERFORM show_k9r_tables.
|
||||||
|
|
||||||
|
FORM show_tkedrs.
|
||||||
|
|
||||||
|
DATA lt_tkedrs TYPE STANDARD TABLE OF tkedrs.
|
||||||
|
|
||||||
|
SELECT *
|
||||||
|
FROM tkedrs
|
||||||
|
INTO TABLE lt_tkedrs.
|
||||||
|
|
||||||
|
WRITE: / '=== TKEDRS Diagnose ==='.
|
||||||
|
WRITE: / 'Gelesene TKEDRS-Zeilen:', lines( lt_tkedrs ).
|
||||||
|
ULINE.
|
||||||
|
|
||||||
|
LOOP AT lt_tkedrs ASSIGNING <ls_any>.
|
||||||
|
DATA(lv_skip) = abap_false.
|
||||||
|
|
||||||
|
ASSIGN COMPONENT 'APPLCLASS' OF STRUCTURE <ls_any> TO <lv_any>.
|
||||||
|
IF sy-subrc = 0 AND p_appl IS NOT INITIAL AND <lv_any> <> p_appl.
|
||||||
|
lv_skip = abap_true.
|
||||||
|
ENDIF.
|
||||||
|
IF lv_skip = abap_true.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DATA(lv_interesting) = p_all.
|
||||||
|
PERFORM row_contains USING <ls_any> 'K9R' CHANGING lv_interesting.
|
||||||
|
PERFORM row_contains USING <ls_any> 'PAPH' CHANGING lv_interesting.
|
||||||
|
PERFORM row_contains USING <ls_any> 'WWPFA' CHANGING lv_interesting.
|
||||||
|
PERFORM row_contains USING <ls_any> 'PROD' CHANGING lv_interesting.
|
||||||
|
PERFORM row_contains USING <ls_any> 'FAMIL' CHANGING lv_interesting.
|
||||||
|
|
||||||
|
IF lv_interesting <> abap_true.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
WRITE: / '--- TKEDRS Kandidat ---'.
|
||||||
|
PERFORM print_non_initial_components USING <ls_any>.
|
||||||
|
ULINE.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
|
|
||||||
|
FORM show_k9r_tables.
|
||||||
|
|
||||||
|
DATA: lt_dd02l TYPE STANDARD TABLE OF dd02l,
|
||||||
|
lv_count TYPE i.
|
||||||
|
|
||||||
|
SELECT *
|
||||||
|
FROM dd02l
|
||||||
|
INTO TABLE lt_dd02l
|
||||||
|
WHERE tabname LIKE p_like
|
||||||
|
AND as4local = 'A'.
|
||||||
|
|
||||||
|
SORT lt_dd02l BY tabname.
|
||||||
|
|
||||||
|
WRITE: / '=== K9R Tabellen ==='.
|
||||||
|
WRITE: / 'Gefundene Tabellen:', lines( lt_dd02l ).
|
||||||
|
ULINE.
|
||||||
|
|
||||||
|
LOOP AT lt_dd02l INTO DATA(ls_dd02l).
|
||||||
|
CLEAR lv_count.
|
||||||
|
TRY.
|
||||||
|
SELECT COUNT( * )
|
||||||
|
FROM (ls_dd02l-tabname)
|
||||||
|
INTO lv_count.
|
||||||
|
CATCH cx_sy_dynamic_osql_error.
|
||||||
|
CONTINUE.
|
||||||
|
ENDTRY.
|
||||||
|
|
||||||
|
IF lv_count = 0 AND p_all <> abap_true.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
WRITE: / '--- Tabelle:', ls_dd02l-tabname,
|
||||||
|
'Klasse:', ls_dd02l-tabclass,
|
||||||
|
'Zeilen:', lv_count.
|
||||||
|
|
||||||
|
PERFORM show_fields USING ls_dd02l-tabname.
|
||||||
|
PERFORM show_samples USING ls_dd02l-tabname p_rows.
|
||||||
|
ULINE.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
|
|
||||||
|
FORM show_fields USING iv_tab TYPE dd02l-tabname.
|
||||||
|
|
||||||
|
DATA lt_dfies TYPE STANDARD TABLE OF dfies.
|
||||||
|
|
||||||
|
CALL FUNCTION 'DDIF_FIELDINFO_GET'
|
||||||
|
EXPORTING
|
||||||
|
tabname = iv_tab
|
||||||
|
TABLES
|
||||||
|
dfies_tab = lt_dfies
|
||||||
|
EXCEPTIONS
|
||||||
|
OTHERS = 1.
|
||||||
|
IF sy-subrc <> 0.
|
||||||
|
WRITE: / 'Feldliste nicht lesbar.'.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
WRITE: / 'Felder:'.
|
||||||
|
LOOP AT lt_dfies INTO DATA(ls_f).
|
||||||
|
WRITE: / ls_f-position,
|
||||||
|
6 ls_f-fieldname,
|
||||||
|
28 ls_f-datatype,
|
||||||
|
38 ls_f-leng,
|
||||||
|
48 ls_f-fieldtext.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
|
|
||||||
|
FORM show_samples USING iv_tab TYPE dd02l-tabname
|
||||||
|
iv_rows TYPE i.
|
||||||
|
|
||||||
|
DATA lr_table TYPE REF TO data.
|
||||||
|
|
||||||
|
CREATE DATA lr_table TYPE STANDARD TABLE OF (iv_tab).
|
||||||
|
ASSIGN lr_table->* TO <lt_any>.
|
||||||
|
IF sy-subrc <> 0.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
TRY.
|
||||||
|
SELECT *
|
||||||
|
FROM (iv_tab)
|
||||||
|
INTO TABLE <lt_any>
|
||||||
|
UP TO iv_rows ROWS.
|
||||||
|
CATCH cx_sy_dynamic_osql_error.
|
||||||
|
WRITE: / 'Beispielzeilen nicht lesbar.'.
|
||||||
|
RETURN.
|
||||||
|
ENDTRY.
|
||||||
|
|
||||||
|
WRITE: / 'Beispielzeilen:'.
|
||||||
|
LOOP AT <lt_any> ASSIGNING <ls_any>.
|
||||||
|
PERFORM print_non_initial_components USING <ls_any>.
|
||||||
|
ULINE.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
|
|
||||||
|
FORM row_contains USING is_row TYPE any
|
||||||
|
iv_pattern TYPE string
|
||||||
|
CHANGING cv_found TYPE abap_bool.
|
||||||
|
|
||||||
|
IF cv_found = abap_true.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DATA lo_desc TYPE REF TO cl_abap_typedescr.
|
||||||
|
DATA lo_str TYPE REF TO cl_abap_structdescr.
|
||||||
|
|
||||||
|
lo_desc = cl_abap_typedescr=>describe_by_data( is_row ).
|
||||||
|
lo_str ?= lo_desc.
|
||||||
|
|
||||||
|
LOOP AT lo_str->components INTO DATA(ls_comp).
|
||||||
|
ASSIGN COMPONENT ls_comp-name OF STRUCTURE is_row TO <lv_any>.
|
||||||
|
IF sy-subrc <> 0 OR <lv_any> IS INITIAL.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DATA(lv_value) = |{ <lv_any> }|.
|
||||||
|
TRANSLATE lv_value TO UPPER CASE.
|
||||||
|
|
||||||
|
IF lv_value CS iv_pattern.
|
||||||
|
cv_found = abap_true.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
|
|
||||||
|
FORM print_non_initial_components USING is_row TYPE any.
|
||||||
|
|
||||||
|
DATA lo_desc TYPE REF TO cl_abap_typedescr.
|
||||||
|
DATA lo_str TYPE REF TO cl_abap_structdescr.
|
||||||
|
|
||||||
|
lo_desc = cl_abap_typedescr=>describe_by_data( is_row ).
|
||||||
|
lo_str ?= lo_desc.
|
||||||
|
|
||||||
|
LOOP AT lo_str->components INTO DATA(ls_comp).
|
||||||
|
ASSIGN COMPONENT ls_comp-name OF STRUCTURE is_row TO <lv_any>.
|
||||||
|
IF sy-subrc <> 0 OR <lv_any> IS INITIAL.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
WRITE: / ls_comp-name, 32 '=', 35 <lv_any>.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
*& Report Z_PRODSPARTE_KEDR_RULE_FIND
|
||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
*& Zweck: Findet die technische Ablage der KEDE/KEDR-Regel
|
||||||
|
*& "Produktfamilie aus Produkthierarchie 1".
|
||||||
|
*&
|
||||||
|
*& Hintergrund:
|
||||||
|
*& In KEDE/KEDR ist fachlich PAPH1 von-bis -> WWPFA gepflegt.
|
||||||
|
*& Der Name der generierten Regeltabelle ist systemabhaengig.
|
||||||
|
*& Dieses Diagnoseprogramm sucht DDIC-Tabellen mit PAPH1/WWPFA
|
||||||
|
*& und zeigt Beispielzeilen inkl. moeglicher BIS-/Datumsfelder.
|
||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
REPORT z_prodsparte_kedr_rule_find.
|
||||||
|
|
||||||
|
PARAMETERS: p_max TYPE i DEFAULT 20.
|
||||||
|
|
||||||
|
TYPES: BEGIN OF ty_tab,
|
||||||
|
tabname TYPE dd03l-tabname,
|
||||||
|
END OF ty_tab.
|
||||||
|
|
||||||
|
TYPES: BEGIN OF ty_sample_exact,
|
||||||
|
paph1 TYPE ce11000-paph1,
|
||||||
|
wwpfa TYPE ce11000-wwpfa,
|
||||||
|
END OF ty_sample_exact.
|
||||||
|
|
||||||
|
TYPES: BEGIN OF ty_sample_range,
|
||||||
|
paph1 TYPE ce11000-paph1,
|
||||||
|
paph1_bis TYPE ce11000-paph1,
|
||||||
|
wwpfa TYPE ce11000-wwpfa,
|
||||||
|
END OF ty_sample_range.
|
||||||
|
|
||||||
|
START-OF-SELECTION.
|
||||||
|
|
||||||
|
DATA: lt_paph1 TYPE STANDARD TABLE OF ty_tab,
|
||||||
|
lt_wwpfa TYPE STANDARD TABLE OF ty_tab,
|
||||||
|
lt_tabs TYPE STANDARD TABLE OF ty_tab.
|
||||||
|
|
||||||
|
SELECT DISTINCT tabname
|
||||||
|
FROM dd03l
|
||||||
|
INTO TABLE @lt_paph1
|
||||||
|
WHERE fieldname = 'PAPH1'
|
||||||
|
AND as4local = 'A'.
|
||||||
|
|
||||||
|
SELECT DISTINCT tabname
|
||||||
|
FROM dd03l
|
||||||
|
INTO TABLE @lt_wwpfa
|
||||||
|
WHERE fieldname = 'WWPFA'
|
||||||
|
AND as4local = 'A'.
|
||||||
|
|
||||||
|
SORT lt_paph1 BY tabname.
|
||||||
|
SORT lt_wwpfa BY tabname.
|
||||||
|
|
||||||
|
LOOP AT lt_paph1 INTO DATA(ls_paph1).
|
||||||
|
READ TABLE lt_wwpfa TRANSPORTING NO FIELDS
|
||||||
|
WITH KEY tabname = ls_paph1-tabname BINARY SEARCH.
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
APPEND ls_paph1 TO lt_tabs.
|
||||||
|
ENDIF.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
SORT lt_tabs BY tabname.
|
||||||
|
DELETE ADJACENT DUPLICATES FROM lt_tabs COMPARING tabname.
|
||||||
|
|
||||||
|
WRITE: / 'Tabellen mit Feldern PAPH1 und WWPFA:', lines( lt_tabs ).
|
||||||
|
ULINE.
|
||||||
|
|
||||||
|
LOOP AT lt_tabs INTO DATA(ls_tab).
|
||||||
|
PERFORM show_candidate USING ls_tab-tabname p_max.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
FORM show_candidate USING iv_tab TYPE dd03l-tabname
|
||||||
|
iv_max TYPE i.
|
||||||
|
|
||||||
|
DATA: lt_dfies TYPE STANDARD TABLE OF dfies,
|
||||||
|
lv_count TYPE i,
|
||||||
|
lv_has_bis TYPE abap_bool.
|
||||||
|
|
||||||
|
CALL FUNCTION 'DDIF_FIELDINFO_GET'
|
||||||
|
EXPORTING
|
||||||
|
tabname = iv_tab
|
||||||
|
TABLES
|
||||||
|
dfies_tab = lt_dfies
|
||||||
|
EXCEPTIONS
|
||||||
|
OTHERS = 1.
|
||||||
|
IF sy-subrc <> 0.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
READ TABLE lt_dfies TRANSPORTING NO FIELDS
|
||||||
|
WITH KEY fieldname = 'PAPH1_BIS'.
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
lv_has_bis = abap_true.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
TRY.
|
||||||
|
SELECT COUNT( * )
|
||||||
|
FROM (iv_tab)
|
||||||
|
INTO @lv_count
|
||||||
|
WHERE paph1 <> @space
|
||||||
|
AND wwpfa <> @space.
|
||||||
|
CATCH cx_sy_dynamic_osql_error.
|
||||||
|
RETURN.
|
||||||
|
ENDTRY.
|
||||||
|
|
||||||
|
IF lv_count = 0.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
WRITE: / '--- Kandidat:', iv_tab, 'Saetze mit PAPH1/WWPFA:', lv_count.
|
||||||
|
WRITE: / 'Relevante Felder:'.
|
||||||
|
LOOP AT lt_dfies INTO DATA(ls_f)
|
||||||
|
WHERE fieldname CS 'PAPH'
|
||||||
|
OR fieldname CS 'WWPFA'
|
||||||
|
OR fieldname CS 'BIS'
|
||||||
|
OR fieldname CS 'LOW'
|
||||||
|
OR fieldname CS 'HIGH'
|
||||||
|
OR fieldname CS 'VON'
|
||||||
|
OR fieldname CS 'DAT'
|
||||||
|
OR fieldname CS 'DEL'
|
||||||
|
OR fieldname CS 'LOE'.
|
||||||
|
WRITE: / ls_f-fieldname, 18 ls_f-datatype, 28 ls_f-leng, 36 ls_f-fieldtext.
|
||||||
|
ENDLOOP.
|
||||||
|
ULINE.
|
||||||
|
|
||||||
|
IF lv_has_bis = abap_true.
|
||||||
|
DATA lt_range TYPE STANDARD TABLE OF ty_sample_range.
|
||||||
|
TRY.
|
||||||
|
SELECT paph1, paph1_bis, wwpfa
|
||||||
|
FROM (iv_tab)
|
||||||
|
INTO TABLE @lt_range
|
||||||
|
UP TO @iv_max ROWS
|
||||||
|
WHERE paph1 <> @space
|
||||||
|
AND wwpfa <> @space.
|
||||||
|
WRITE: / 'Beispiele PAPH1 / PAPH1_BIS / WWPFA:'.
|
||||||
|
LOOP AT lt_range INTO DATA(ls_range).
|
||||||
|
WRITE: / ls_range-paph1, 12 ls_range-paph1_bis, 24 ls_range-wwpfa.
|
||||||
|
ENDLOOP.
|
||||||
|
CATCH cx_sy_dynamic_osql_error.
|
||||||
|
WRITE: / 'Beispiele mit PAPH1_BIS konnten nicht gelesen werden.'.
|
||||||
|
ENDTRY.
|
||||||
|
ELSE.
|
||||||
|
DATA lt_exact TYPE STANDARD TABLE OF ty_sample_exact.
|
||||||
|
TRY.
|
||||||
|
SELECT paph1, wwpfa
|
||||||
|
FROM (iv_tab)
|
||||||
|
INTO TABLE @lt_exact
|
||||||
|
UP TO @iv_max ROWS
|
||||||
|
WHERE paph1 <> @space
|
||||||
|
AND wwpfa <> @space.
|
||||||
|
WRITE: / 'Beispiele PAPH1 / WWPFA:'.
|
||||||
|
LOOP AT lt_exact INTO DATA(ls_exact).
|
||||||
|
WRITE: / ls_exact-paph1, 12 ls_exact-wwpfa.
|
||||||
|
ENDLOOP.
|
||||||
|
CATCH cx_sy_dynamic_osql_error.
|
||||||
|
WRITE: / 'Beispiele konnten nicht gelesen werden.'.
|
||||||
|
ENDTRY.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
ULINE.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
@@ -0,0 +1,408 @@
|
|||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
*& Report Z_PRODSPARTE_MAP_BUILD
|
||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
*& Zweck: Baut ZPRODSPARTE_MAP direkt aus den KEDE/KEDR-Regeltabellen.
|
||||||
|
*&
|
||||||
|
*& Quelle:
|
||||||
|
*& K9RT761000002 PAPH1 von-bis -> Produktfamilie WWPFA
|
||||||
|
*& K9RT761000003 Produktfamilie von-bis -> Produktsparte WWPSP
|
||||||
|
*&
|
||||||
|
*& Ergebnis:
|
||||||
|
*& ZPRODSPARTE_MAP bleibt eine flache Einzelwert-Tabelle:
|
||||||
|
*& PAPH1 -> WWPFA -> WWPSP
|
||||||
|
*&
|
||||||
|
*& Die Von-bis-Regeln werden vollstaendig in Einzel-PAPH1 expandiert,
|
||||||
|
*& damit ZPRODSPARTE_MAP mindestens alle KEDE-/Data(4)-Referenzcodes
|
||||||
|
*& enthaelt. Reale PAPH1-Codes aus MVKE/CE11000 werden optional
|
||||||
|
*& zusaetzlich aufgenommen, aendern aber nicht die Referenzabdeckung.
|
||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
REPORT z_prodsparte_map_build.
|
||||||
|
|
||||||
|
PARAMETERS: p_vkorg TYPE vkorg,
|
||||||
|
p_vtweg TYPE vtweg.
|
||||||
|
PARAMETERS: p_ce TYPE abap_bool DEFAULT 'X' AS CHECKBOX.
|
||||||
|
PARAMETERS: p_test TYPE abap_bool DEFAULT 'X' AS CHECKBOX.
|
||||||
|
|
||||||
|
TYPES: BEGIN OF ty_code,
|
||||||
|
paph1 TYPE zprodsparte_map-paph1,
|
||||||
|
END OF ty_code.
|
||||||
|
|
||||||
|
TYPES: BEGIN OF ty_pfa_rule,
|
||||||
|
sour1_from TYPE k9rt761000002-sour1_from,
|
||||||
|
sour1_to TYPE k9rt761000002-sour1_to,
|
||||||
|
valid_from TYPE k9rt761000002-valid_from,
|
||||||
|
target1 TYPE k9rt761000002-target1,
|
||||||
|
END OF ty_pfa_rule.
|
||||||
|
|
||||||
|
TYPES: BEGIN OF ty_spa_rule,
|
||||||
|
sour1_from TYPE k9rt761000003-sour1_from,
|
||||||
|
sour1_to TYPE k9rt761000003-sour1_to,
|
||||||
|
valid_from TYPE k9rt761000003-valid_from,
|
||||||
|
target1 TYPE k9rt761000003-target1,
|
||||||
|
END OF ty_spa_rule.
|
||||||
|
|
||||||
|
TYPES tt_code TYPE STANDARD TABLE OF ty_code WITH DEFAULT KEY.
|
||||||
|
TYPES tt_map TYPE STANDARD TABLE OF zprodsparte_map WITH DEFAULT KEY.
|
||||||
|
|
||||||
|
DATA: gt_code TYPE SORTED TABLE OF ty_code WITH UNIQUE KEY paph1,
|
||||||
|
gt_pfa TYPE STANDARD TABLE OF ty_pfa_rule WITH DEFAULT KEY,
|
||||||
|
gt_spa TYPE STANDARD TABLE OF ty_spa_rule WITH DEFAULT KEY.
|
||||||
|
|
||||||
|
DATA: gv_rule_expanded TYPE i,
|
||||||
|
gv_real_added TYPE i,
|
||||||
|
gv_unsupported TYPE i.
|
||||||
|
|
||||||
|
START-OF-SELECTION.
|
||||||
|
|
||||||
|
PERFORM load_rules.
|
||||||
|
PERFORM load_codes.
|
||||||
|
PERFORM build_map.
|
||||||
|
|
||||||
|
FORM load_codes.
|
||||||
|
|
||||||
|
DATA lt_prodh TYPE STANDARD TABLE OF mvke-prodh WITH DEFAULT KEY.
|
||||||
|
DATA lt_ce_paph1 TYPE STANDARD TABLE OF ce11000-paph1 WITH DEFAULT KEY.
|
||||||
|
|
||||||
|
LOOP AT gt_pfa INTO DATA(ls_pfa_rule).
|
||||||
|
PERFORM add_paph1_range USING ls_pfa_rule-sour1_from
|
||||||
|
ls_pfa_rule-sour1_to.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
IF p_vkorg IS INITIAL AND p_vtweg IS INITIAL.
|
||||||
|
SELECT DISTINCT prodh
|
||||||
|
FROM mvke
|
||||||
|
INTO TABLE @lt_prodh
|
||||||
|
WHERE prodh <> @space.
|
||||||
|
ELSEIF p_vkorg IS NOT INITIAL AND p_vtweg IS INITIAL.
|
||||||
|
SELECT DISTINCT prodh
|
||||||
|
FROM mvke
|
||||||
|
INTO TABLE @lt_prodh
|
||||||
|
WHERE vkorg = @p_vkorg
|
||||||
|
AND prodh <> @space.
|
||||||
|
ELSEIF p_vkorg IS INITIAL AND p_vtweg IS NOT INITIAL.
|
||||||
|
SELECT DISTINCT prodh
|
||||||
|
FROM mvke
|
||||||
|
INTO TABLE @lt_prodh
|
||||||
|
WHERE vtweg = @p_vtweg
|
||||||
|
AND prodh <> @space.
|
||||||
|
ELSE.
|
||||||
|
SELECT DISTINCT prodh
|
||||||
|
FROM mvke
|
||||||
|
INTO TABLE @lt_prodh
|
||||||
|
WHERE vkorg = @p_vkorg
|
||||||
|
AND vtweg = @p_vtweg
|
||||||
|
AND prodh <> @space.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
LOOP AT lt_prodh INTO DATA(lv_prodh).
|
||||||
|
INSERT VALUE ty_code( paph1 = lv_prodh(5) ) INTO TABLE gt_code.
|
||||||
|
gv_real_added = gv_real_added + 1.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
IF p_ce = abap_true.
|
||||||
|
SELECT DISTINCT paph1
|
||||||
|
FROM ce11000
|
||||||
|
INTO TABLE @lt_ce_paph1
|
||||||
|
WHERE paph1 <> @space.
|
||||||
|
|
||||||
|
LOOP AT lt_ce_paph1 INTO DATA(lv_paph1).
|
||||||
|
INSERT VALUE ty_code( paph1 = lv_paph1 ) INTO TABLE gt_code.
|
||||||
|
gv_real_added = gv_real_added + 1.
|
||||||
|
ENDLOOP.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
WRITE: / 'PAPH1-Codes aus KEDE-Expansion :', gv_rule_expanded.
|
||||||
|
WRITE: / 'Zusaetzliche reale Code-Versuche:', gv_real_added.
|
||||||
|
WRITE: / 'Nicht expandierbare Bereiche :', gv_unsupported.
|
||||||
|
WRITE: / 'PAPH1-Codes gesamt eindeutig :', lines( gt_code ).
|
||||||
|
|
||||||
|
IF gt_code IS INITIAL.
|
||||||
|
MESSAGE 'Keine PAPH1-Codes gefunden.' TYPE 'E'.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
|
|
||||||
|
FORM add_paph1_range USING iv_from TYPE k9rt761000002-sour1_from
|
||||||
|
iv_to TYPE k9rt761000002-sour1_to.
|
||||||
|
|
||||||
|
DATA: lv_from TYPE string,
|
||||||
|
lv_to TYPE string.
|
||||||
|
|
||||||
|
lv_from = iv_from.
|
||||||
|
lv_to = iv_to.
|
||||||
|
CONDENSE: lv_from, lv_to.
|
||||||
|
|
||||||
|
IF lv_from IS INITIAL.
|
||||||
|
gv_unsupported = gv_unsupported + 1.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF lv_to IS INITIAL.
|
||||||
|
lv_to = lv_from.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF lv_from CO '0123456789'
|
||||||
|
AND lv_to CO '0123456789'.
|
||||||
|
DATA(lv_from_i) = CONV i( lv_from ).
|
||||||
|
DATA(lv_to_i) = CONV i( lv_to ).
|
||||||
|
DATA(lv_width) = strlen( lv_from ).
|
||||||
|
|
||||||
|
IF lv_to_i < lv_from_i.
|
||||||
|
gv_unsupported = gv_unsupported + 1.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DATA(lv_count) = lv_to_i - lv_from_i + 1.
|
||||||
|
|
||||||
|
DO lv_count TIMES.
|
||||||
|
DATA(lv_num) = lv_from_i + sy-index - 1.
|
||||||
|
DATA(lv_paph1_num) =
|
||||||
|
|{ lv_num ALIGN = RIGHT PAD = '0' WIDTH = lv_width }|.
|
||||||
|
INSERT VALUE ty_code( paph1 = lv_paph1_num ) INTO TABLE gt_code.
|
||||||
|
gv_rule_expanded = gv_rule_expanded + 1.
|
||||||
|
ENDDO.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF strlen( lv_from ) = 4
|
||||||
|
AND strlen( lv_to ) = 4
|
||||||
|
AND lv_from(2) = lv_to(2).
|
||||||
|
DATA(lv_alpha) = `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
|
||||||
|
DATA(lv_alpha_len) = strlen( lv_alpha ).
|
||||||
|
DATA(lv_prefix) = lv_from(2).
|
||||||
|
|
||||||
|
DO lv_alpha_len TIMES.
|
||||||
|
DATA(lv_off3) = sy-index - 1.
|
||||||
|
DATA(lv_c3) = lv_alpha+lv_off3(1).
|
||||||
|
|
||||||
|
DO lv_alpha_len TIMES.
|
||||||
|
DATA(lv_off4) = sy-index - 1.
|
||||||
|
DATA(lv_c4) = lv_alpha+lv_off4(1).
|
||||||
|
DATA(lv_paph1_alpha) = |{ lv_prefix }{ lv_c3 }{ lv_c4 }|.
|
||||||
|
|
||||||
|
IF lv_paph1_alpha >= lv_from
|
||||||
|
AND lv_paph1_alpha <= lv_to.
|
||||||
|
INSERT VALUE ty_code( paph1 = lv_paph1_alpha ) INTO TABLE gt_code.
|
||||||
|
gv_rule_expanded = gv_rule_expanded + 1.
|
||||||
|
ENDIF.
|
||||||
|
ENDDO.
|
||||||
|
ENDDO.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
gv_unsupported = gv_unsupported + 1.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
|
|
||||||
|
FORM load_rules.
|
||||||
|
|
||||||
|
SELECT sour1_from, sour1_to, valid_from, target1
|
||||||
|
FROM k9rt761000002
|
||||||
|
INTO TABLE @gt_pfa
|
||||||
|
WHERE sour1_from <> @space
|
||||||
|
AND target1 <> @space
|
||||||
|
AND delete_flg <> 'X'.
|
||||||
|
|
||||||
|
SELECT sour1_from, sour1_to, valid_from, target1
|
||||||
|
FROM k9rt761000003
|
||||||
|
INTO TABLE @gt_spa
|
||||||
|
WHERE sour1_from <> @space
|
||||||
|
AND target1 <> @space
|
||||||
|
AND delete_flg <> 'X'.
|
||||||
|
|
||||||
|
SORT gt_pfa BY valid_from DESCENDING sour1_from sour1_to.
|
||||||
|
SORT gt_spa BY valid_from DESCENDING sour1_from sour1_to.
|
||||||
|
|
||||||
|
WRITE: / 'KEDR-Regeln PAPH1->WWPFA :', lines( gt_pfa ).
|
||||||
|
WRITE: / 'KEDR-Regeln WWPFA->WWPSP :', lines( gt_spa ).
|
||||||
|
ULINE.
|
||||||
|
|
||||||
|
IF gt_pfa IS INITIAL.
|
||||||
|
MESSAGE 'Keine KEDE-Regeln in K9RT761000002 gefunden.' TYPE 'E'.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF gt_spa IS INITIAL.
|
||||||
|
MESSAGE 'Keine KEDE-Regeln in K9RT761000003 gefunden.' TYPE 'E'.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
|
|
||||||
|
FORM build_map.
|
||||||
|
|
||||||
|
DATA: lt_insert TYPE tt_map,
|
||||||
|
lt_no_pfa TYPE tt_code,
|
||||||
|
lt_no_spa TYPE tt_code.
|
||||||
|
|
||||||
|
LOOP AT gt_code INTO DATA(ls_code).
|
||||||
|
DATA: lv_wwpfa TYPE zprodsparte_map-wwpfa,
|
||||||
|
lv_wwpsp TYPE zprodsparte_map-wwpsp,
|
||||||
|
lv_found_pfa TYPE abap_bool,
|
||||||
|
lv_found_spa TYPE abap_bool,
|
||||||
|
lv_best_date TYPE dats,
|
||||||
|
lv_best_exact TYPE abap_bool.
|
||||||
|
|
||||||
|
CLEAR: lv_wwpfa,
|
||||||
|
lv_wwpsp,
|
||||||
|
lv_found_pfa,
|
||||||
|
lv_found_spa,
|
||||||
|
lv_best_date,
|
||||||
|
lv_best_exact.
|
||||||
|
|
||||||
|
LOOP AT gt_pfa INTO DATA(ls_pfa).
|
||||||
|
DATA(lv_pfa_to) = ls_pfa-sour1_to.
|
||||||
|
IF lv_pfa_to IS INITIAL.
|
||||||
|
lv_pfa_to = ls_pfa-sour1_from.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF ls_code-paph1 < ls_pfa-sour1_from
|
||||||
|
OR ls_code-paph1 > lv_pfa_to.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DATA(lv_exact_pfa) = abap_false.
|
||||||
|
IF ls_code-paph1 = ls_pfa-sour1_from
|
||||||
|
AND ls_code-paph1 = lv_pfa_to.
|
||||||
|
lv_exact_pfa = abap_true.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF lv_found_pfa = abap_false
|
||||||
|
OR ls_pfa-valid_from > lv_best_date
|
||||||
|
OR ( ls_pfa-valid_from = lv_best_date
|
||||||
|
AND lv_best_exact = abap_false
|
||||||
|
AND lv_exact_pfa = abap_true ).
|
||||||
|
lv_found_pfa = abap_true.
|
||||||
|
lv_best_date = ls_pfa-valid_from.
|
||||||
|
lv_best_exact = lv_exact_pfa.
|
||||||
|
lv_wwpfa = ls_pfa-target1.
|
||||||
|
ENDIF.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
IF lv_found_pfa = abap_false.
|
||||||
|
APPEND ls_code TO lt_no_pfa.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
CLEAR: lv_best_date, lv_best_exact.
|
||||||
|
|
||||||
|
LOOP AT gt_spa INTO DATA(ls_spa).
|
||||||
|
DATA(lv_spa_to) = ls_spa-sour1_to.
|
||||||
|
IF lv_spa_to IS INITIAL.
|
||||||
|
lv_spa_to = ls_spa-sour1_from.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF lv_wwpfa < ls_spa-sour1_from
|
||||||
|
OR lv_wwpfa > lv_spa_to.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DATA(lv_exact_spa) = abap_false.
|
||||||
|
IF lv_wwpfa = ls_spa-sour1_from
|
||||||
|
AND lv_wwpfa = lv_spa_to.
|
||||||
|
lv_exact_spa = abap_true.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF lv_found_spa = abap_false
|
||||||
|
OR ls_spa-valid_from > lv_best_date
|
||||||
|
OR ( ls_spa-valid_from = lv_best_date
|
||||||
|
AND lv_best_exact = abap_false
|
||||||
|
AND lv_exact_spa = abap_true ).
|
||||||
|
lv_found_spa = abap_true.
|
||||||
|
lv_best_date = ls_spa-valid_from.
|
||||||
|
lv_best_exact = lv_exact_spa.
|
||||||
|
lv_wwpsp = ls_spa-target1.
|
||||||
|
ENDIF.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
IF lv_found_spa = abap_false.
|
||||||
|
APPEND ls_code TO lt_no_spa.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
APPEND VALUE zprodsparte_map(
|
||||||
|
paph1 = ls_code-paph1
|
||||||
|
wwpfa = lv_wwpfa
|
||||||
|
wwpsp = lv_wwpsp
|
||||||
|
crdate = sy-datum
|
||||||
|
cruser = sy-uname ) TO lt_insert.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
WRITE: / 'Saetze fuer ZPRODSPARTE_MAP :', lines( lt_insert ).
|
||||||
|
WRITE: / 'PAPH1 ohne Produktfamilie :', lines( lt_no_pfa ).
|
||||||
|
WRITE: / 'PAPH1 ohne Produktsparte :', lines( lt_no_spa ).
|
||||||
|
ULINE.
|
||||||
|
|
||||||
|
PERFORM show_preview USING lt_insert lt_no_pfa lt_no_spa.
|
||||||
|
|
||||||
|
IF lt_insert IS INITIAL.
|
||||||
|
WRITE: / 'Keine Saetze zum Schreiben. Tabelle bleibt unveraendert.'.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF p_test = abap_true.
|
||||||
|
WRITE: / 'TESTLAUF - keine DB-Aenderung.'.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DELETE FROM zprodsparte_map.
|
||||||
|
INSERT zprodsparte_map FROM TABLE lt_insert.
|
||||||
|
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
COMMIT WORK.
|
||||||
|
WRITE: / lines( lt_insert ), 'Saetze in ZPRODSPARTE_MAP geschrieben.'.
|
||||||
|
ELSE.
|
||||||
|
ROLLBACK WORK.
|
||||||
|
WRITE: / 'Fehler beim Schreiben, sy-subrc=', sy-subrc.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
|
|
||||||
|
FORM show_preview USING it_insert TYPE tt_map
|
||||||
|
it_no_pfa TYPE tt_code
|
||||||
|
it_no_spa TYPE tt_code.
|
||||||
|
|
||||||
|
FIELD-SYMBOLS: <ls_insert> TYPE zprodsparte_map,
|
||||||
|
<ls_code> TYPE ty_code.
|
||||||
|
|
||||||
|
WRITE: / '=== Vorschau Mapping max. 30 ==='.
|
||||||
|
WRITE: / 'PAPH1', 10 'WWPFA', 20 'WWPSP'.
|
||||||
|
ULINE.
|
||||||
|
|
||||||
|
DATA lv_i TYPE i.
|
||||||
|
LOOP AT it_insert ASSIGNING <ls_insert>.
|
||||||
|
lv_i = lv_i + 1.
|
||||||
|
IF lv_i > 30.
|
||||||
|
EXIT.
|
||||||
|
ENDIF.
|
||||||
|
WRITE: / <ls_insert>-paph1,
|
||||||
|
10 <ls_insert>-wwpfa,
|
||||||
|
20 <ls_insert>-wwpsp.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
IF it_no_pfa IS NOT INITIAL.
|
||||||
|
ULINE.
|
||||||
|
WRITE: / '=== Erste PAPH1 ohne Produktfamilie ==='.
|
||||||
|
CLEAR lv_i.
|
||||||
|
LOOP AT it_no_pfa ASSIGNING <ls_code>.
|
||||||
|
lv_i = lv_i + 1.
|
||||||
|
IF lv_i > 30.
|
||||||
|
EXIT.
|
||||||
|
ENDIF.
|
||||||
|
WRITE: / <ls_code>-paph1.
|
||||||
|
ENDLOOP.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF it_no_spa IS NOT INITIAL.
|
||||||
|
ULINE.
|
||||||
|
WRITE: / '=== Erste PAPH1 ohne Produktsparte ==='.
|
||||||
|
CLEAR lv_i.
|
||||||
|
LOOP AT it_no_spa ASSIGNING <ls_code>.
|
||||||
|
lv_i = lv_i + 1.
|
||||||
|
IF lv_i > 30.
|
||||||
|
EXIT.
|
||||||
|
ENDIF.
|
||||||
|
WRITE: / <ls_code>-paph1.
|
||||||
|
ENDLOOP.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
ULINE.
|
||||||
|
|
||||||
|
ENDFORM.
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
*& Report Z_PRODSPARTE_MAP_EXPORT
|
||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
*& Zweck: Exportiert die flache Mapping-Tabelle ZPRODSPARTE_MAP zur
|
||||||
|
*& Kontrolle gegen Data(4)/KEDE-Referenz.
|
||||||
|
*&
|
||||||
|
*& Erwartung nach Z_PRODSPARTE_MAP_BUILD:
|
||||||
|
*& Alle Data(4)-Referenzcodes muessen hier mit gleicher WWPFA stehen.
|
||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
REPORT z_prodsparte_map_export.
|
||||||
|
|
||||||
|
PARAMETERS: p_file TYPE string LOWER CASE
|
||||||
|
DEFAULT 'C:\temp\zprodsparte_map_export.csv'.
|
||||||
|
|
||||||
|
START-OF-SELECTION.
|
||||||
|
|
||||||
|
SELECT paph1, wwpfa, wwpsp
|
||||||
|
FROM zprodsparte_map
|
||||||
|
INTO TABLE @DATA(lt_map)
|
||||||
|
WHERE paph1 <> @space.
|
||||||
|
|
||||||
|
SORT lt_map BY paph1.
|
||||||
|
|
||||||
|
DATA: lt_csv TYPE STANDARD TABLE OF string,
|
||||||
|
lv_sep TYPE c LENGTH 1.
|
||||||
|
|
||||||
|
lv_sep = ';'.
|
||||||
|
|
||||||
|
APPEND |PAPH1{ lv_sep }WWPFA{ lv_sep }WWPSP| TO lt_csv.
|
||||||
|
|
||||||
|
LOOP AT lt_map INTO DATA(ls_map).
|
||||||
|
APPEND |{ ls_map-paph1 }{ lv_sep }{ ls_map-wwpfa }{ lv_sep }{ ls_map-wwpsp }|
|
||||||
|
TO lt_csv.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
cl_gui_frontend_services=>gui_download(
|
||||||
|
EXPORTING
|
||||||
|
filename = p_file
|
||||||
|
filetype = 'ASC'
|
||||||
|
CHANGING
|
||||||
|
data_tab = lt_csv
|
||||||
|
EXCEPTIONS
|
||||||
|
OTHERS = 1 ).
|
||||||
|
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
WRITE: / lines( lt_map ), 'Mapping-Saetze exportiert nach', p_file.
|
||||||
|
ELSE.
|
||||||
|
WRITE: / 'Download-Fehler, sy-subrc=', sy-subrc.
|
||||||
|
ENDIF.
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
*& Report Z_PRODSPARTE_MAP_IMPORT
|
||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
*& Zweck: Importiert die in KEDE/KEDR gepflegten Regeln
|
||||||
|
*& PAPH1 von-bis -> WWPFA aus einem Regel-Export und expandiert
|
||||||
|
*& Bereiche in Einzelwerte.
|
||||||
|
*&
|
||||||
|
*& Ergebnis in ZPRODSPARTE_MAP:
|
||||||
|
*& PAPH1 -> WWPFA -> WWPSP
|
||||||
|
*&
|
||||||
|
*& Keine Tabellenerweiterung fuer PAPH1_BIS/GUELTAB erforderlich.
|
||||||
|
*& Die Von-bis-Information wird nur beim Import verarbeitet.
|
||||||
|
*&---------------------------------------------------------------------*
|
||||||
|
REPORT z_prodsparte_map_import.
|
||||||
|
|
||||||
|
PARAMETERS: p_file TYPE string LOWER CASE
|
||||||
|
DEFAULT 'C:\temp\kede_regeln.csv' OBLIGATORY.
|
||||||
|
PARAMETERS: p_sep TYPE c LENGTH 1 DEFAULT ';'.
|
||||||
|
PARAMETERS: p_test TYPE abap_bool DEFAULT 'X' AS CHECKBOX.
|
||||||
|
|
||||||
|
TYPES: BEGIN OF ty_rule,
|
||||||
|
paph1 TYPE zprodsparte_map-paph1,
|
||||||
|
wwpfa TYPE zprodsparte_map-wwpfa,
|
||||||
|
gueltab TYPE dats,
|
||||||
|
END OF ty_rule.
|
||||||
|
|
||||||
|
TYPES: BEGIN OF ty_fs,
|
||||||
|
wwpfa TYPE ce11000-wwpfa,
|
||||||
|
wwpsp TYPE ce11000-wwpsp,
|
||||||
|
cnt TYPE i,
|
||||||
|
END OF ty_fs.
|
||||||
|
|
||||||
|
START-OF-SELECTION.
|
||||||
|
|
||||||
|
DATA lt_raw TYPE STANDARD TABLE OF string.
|
||||||
|
|
||||||
|
cl_gui_frontend_services=>gui_upload(
|
||||||
|
EXPORTING
|
||||||
|
filename = p_file
|
||||||
|
filetype = 'ASC'
|
||||||
|
CHANGING
|
||||||
|
data_tab = lt_raw
|
||||||
|
EXCEPTIONS
|
||||||
|
OTHERS = 1 ).
|
||||||
|
IF sy-subrc <> 0.
|
||||||
|
MESSAGE |Datei { p_file } nicht lesbar, sy-subrc={ sy-subrc }| TYPE 'E'.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DATA: lt_rule TYPE STANDARD TABLE OF ty_rule,
|
||||||
|
lt_fields TYPE STANDARD TABLE OF string,
|
||||||
|
lv_from TYPE string,
|
||||||
|
lv_to TYPE string,
|
||||||
|
lv_date TYPE string,
|
||||||
|
lv_del TYPE string,
|
||||||
|
lv_wwpfa TYPE string,
|
||||||
|
lv_gueltab TYPE dats,
|
||||||
|
lv_skipped TYPE i,
|
||||||
|
lv_expanded TYPE i,
|
||||||
|
lv_unsupported TYPE i.
|
||||||
|
|
||||||
|
LOOP AT lt_raw INTO DATA(lv_line).
|
||||||
|
CLEAR: lt_fields, lv_from, lv_to, lv_date, lv_del, lv_wwpfa, lv_gueltab.
|
||||||
|
SPLIT lv_line AT p_sep INTO TABLE lt_fields.
|
||||||
|
|
||||||
|
IF lines( lt_fields ) < 7.
|
||||||
|
lv_skipped = lv_skipped + 1.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
READ TABLE lt_fields INTO lv_from INDEX 1.
|
||||||
|
READ TABLE lt_fields INTO lv_to INDEX 3.
|
||||||
|
READ TABLE lt_fields INTO lv_date INDEX 5.
|
||||||
|
READ TABLE lt_fields INTO lv_del INDEX 6.
|
||||||
|
READ TABLE lt_fields INTO lv_wwpfa INDEX 7.
|
||||||
|
|
||||||
|
CONDENSE: lv_from, lv_to, lv_date, lv_del, lv_wwpfa.
|
||||||
|
|
||||||
|
IF lv_from IS INITIAL
|
||||||
|
OR lv_wwpfa IS INITIAL
|
||||||
|
OR lv_from CS 'ProdHierarchie'
|
||||||
|
OR lv_from CS 'Seite'.
|
||||||
|
lv_skipped = lv_skipped + 1.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF lv_del = 'X' OR lv_del = 'x'.
|
||||||
|
lv_skipped = lv_skipped + 1.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF strlen( lv_date ) = 10
|
||||||
|
AND lv_date+2(1) = '.'
|
||||||
|
AND lv_date+5(1) = '.'.
|
||||||
|
lv_gueltab = |{ lv_date+6(4) }{ lv_date+3(2) }{ lv_date(2) }|.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF lv_wwpfa CO '0123456789'
|
||||||
|
AND strlen( lv_wwpfa ) < 4.
|
||||||
|
lv_wwpfa = |{ lv_wwpfa ALIGN = RIGHT PAD = '0' WIDTH = 4 }|.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF lv_to IS INITIAL.
|
||||||
|
APPEND VALUE ty_rule(
|
||||||
|
paph1 = lv_from
|
||||||
|
wwpfa = lv_wwpfa
|
||||||
|
gueltab = lv_gueltab ) TO lt_rule.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
IF lv_from CO '0123456789'
|
||||||
|
AND lv_to CO '0123456789'.
|
||||||
|
DATA(lv_from_i) = CONV i( lv_from ).
|
||||||
|
DATA(lv_to_i) = CONV i( lv_to ).
|
||||||
|
DATA(lv_width) = strlen( lv_from ).
|
||||||
|
|
||||||
|
IF lv_to_i < lv_from_i.
|
||||||
|
lv_unsupported = lv_unsupported + 1.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DATA(lv_count) = lv_to_i - lv_from_i + 1.
|
||||||
|
|
||||||
|
DO lv_count TIMES.
|
||||||
|
DATA(lv_num) = lv_from_i + sy-index - 1.
|
||||||
|
DATA(lv_paph1_num) =
|
||||||
|
|{ lv_num ALIGN = RIGHT PAD = '0' WIDTH = lv_width }|.
|
||||||
|
APPEND VALUE ty_rule(
|
||||||
|
paph1 = lv_paph1_num
|
||||||
|
wwpfa = lv_wwpfa
|
||||||
|
gueltab = lv_gueltab ) TO lt_rule.
|
||||||
|
lv_expanded = lv_expanded + 1.
|
||||||
|
ENDDO.
|
||||||
|
CONTINUE.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
" Bekannter alphanumerischer Finance-Bereich: z.B. 09B0 bis 09M4.
|
||||||
|
IF strlen( lv_from ) = 4
|
||||||
|
AND strlen( lv_to ) = 4
|
||||||
|
AND lv_from(2) = lv_to(2).
|
||||||
|
DATA(lv_alpha) = `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
|
||||||
|
DATA(lv_prefix) = lv_from(2).
|
||||||
|
|
||||||
|
DO strlen( lv_alpha ) TIMES.
|
||||||
|
DATA(lv_off3) = sy-index - 1.
|
||||||
|
DATA(lv_c3) = lv_alpha+lv_off3(1).
|
||||||
|
|
||||||
|
DO strlen( lv_alpha ) TIMES.
|
||||||
|
DATA(lv_off4) = sy-index - 1.
|
||||||
|
DATA(lv_c4) = lv_alpha+lv_off4(1).
|
||||||
|
DATA(lv_paph1_alpha) = |{ lv_prefix }{ lv_c3 }{ lv_c4 }|.
|
||||||
|
|
||||||
|
IF lv_paph1_alpha >= lv_from
|
||||||
|
AND lv_paph1_alpha <= lv_to.
|
||||||
|
APPEND VALUE ty_rule(
|
||||||
|
paph1 = lv_paph1_alpha
|
||||||
|
wwpfa = lv_wwpfa
|
||||||
|
gueltab = lv_gueltab ) TO lt_rule.
|
||||||
|
lv_expanded = lv_expanded + 1.
|
||||||
|
ENDIF.
|
||||||
|
ENDDO.
|
||||||
|
ENDDO.
|
||||||
|
ELSE.
|
||||||
|
lv_unsupported = lv_unsupported + 1.
|
||||||
|
ENDIF.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
SORT lt_rule BY paph1 gueltab DESCENDING.
|
||||||
|
DELETE ADJACENT DUPLICATES FROM lt_rule COMPARING paph1.
|
||||||
|
|
||||||
|
WRITE: / 'CSV-Zeilen gelesen :', lines( lt_raw ).
|
||||||
|
WRITE: / 'Einzelwerte nach Expansion :', lines( lt_rule ).
|
||||||
|
WRITE: / 'Expandierte Bereichswerte :', lv_expanded.
|
||||||
|
WRITE: / 'Uebersprungene Zeilen :', lv_skipped.
|
||||||
|
WRITE: / 'Nicht expandierbare Bereiche:', lv_unsupported.
|
||||||
|
ULINE.
|
||||||
|
|
||||||
|
IF lt_rule IS INITIAL.
|
||||||
|
MESSAGE 'Keine gueltigen Regeln gefunden.' TYPE 'E'.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DATA: lt_fs TYPE STANDARD TABLE OF ty_fs,
|
||||||
|
ls_fs TYPE ty_fs.
|
||||||
|
|
||||||
|
SELECT DISTINCT wwpfa, wwpsp
|
||||||
|
FROM ce11000
|
||||||
|
INTO TABLE @DATA(lt_combo)
|
||||||
|
WHERE wwpfa <> @space
|
||||||
|
AND wwpsp <> @space.
|
||||||
|
|
||||||
|
LOOP AT lt_combo INTO DATA(ls_combo).
|
||||||
|
READ TABLE lt_fs INTO ls_fs WITH KEY wwpfa = ls_combo-wwpfa.
|
||||||
|
IF sy-subrc <> 0.
|
||||||
|
ls_fs-wwpfa = ls_combo-wwpfa.
|
||||||
|
ls_fs-wwpsp = ls_combo-wwpsp.
|
||||||
|
ls_fs-cnt = 1.
|
||||||
|
APPEND ls_fs TO lt_fs.
|
||||||
|
ELSEIF ls_fs-wwpsp <> ls_combo-wwpsp.
|
||||||
|
ls_fs-cnt = ls_fs-cnt + 1.
|
||||||
|
MODIFY lt_fs FROM ls_fs TRANSPORTING cnt
|
||||||
|
WHERE wwpfa = ls_combo-wwpfa.
|
||||||
|
ENDIF.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
LOOP AT lt_fs INTO ls_fs WHERE cnt > 1.
|
||||||
|
WRITE: / 'WARNUNG: Familie', ls_fs-wwpfa,
|
||||||
|
'hat mehrere Sparten in CE11000 - WWPSP bleibt leer.'.
|
||||||
|
ENDLOOP.
|
||||||
|
DELETE lt_fs WHERE cnt > 1.
|
||||||
|
SORT lt_fs BY wwpfa.
|
||||||
|
|
||||||
|
DATA: lt_insert TYPE STANDARD TABLE OF zprodsparte_map,
|
||||||
|
lv_no_fs TYPE i.
|
||||||
|
|
||||||
|
LOOP AT lt_rule INTO DATA(ls_rule).
|
||||||
|
READ TABLE lt_fs INTO ls_fs
|
||||||
|
WITH KEY wwpfa = ls_rule-wwpfa BINARY SEARCH.
|
||||||
|
|
||||||
|
DATA(ls_insert) = VALUE zprodsparte_map(
|
||||||
|
paph1 = ls_rule-paph1
|
||||||
|
wwpfa = ls_rule-wwpfa
|
||||||
|
crdate = sy-datum
|
||||||
|
cruser = sy-uname ).
|
||||||
|
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
ls_insert-wwpsp = ls_fs-wwpsp.
|
||||||
|
ELSE.
|
||||||
|
lv_no_fs = lv_no_fs + 1.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
APPEND ls_insert TO lt_insert.
|
||||||
|
ENDLOOP.
|
||||||
|
|
||||||
|
WRITE: / 'Familien ohne eindeutige Sparte:', lv_no_fs.
|
||||||
|
ULINE.
|
||||||
|
WRITE: / '=== Vorschau max. 30 ==='.
|
||||||
|
WRITE: / 'PAPH1', 10 'WWPFA', 20 'WWPSP'.
|
||||||
|
ULINE.
|
||||||
|
|
||||||
|
DATA lv_i TYPE i.
|
||||||
|
LOOP AT lt_insert INTO DATA(ls_preview).
|
||||||
|
lv_i = lv_i + 1.
|
||||||
|
IF lv_i > 30.
|
||||||
|
EXIT.
|
||||||
|
ENDIF.
|
||||||
|
WRITE: / ls_preview-paph1,
|
||||||
|
10 ls_preview-wwpfa,
|
||||||
|
20 ls_preview-wwpsp.
|
||||||
|
ENDLOOP.
|
||||||
|
ULINE.
|
||||||
|
|
||||||
|
IF p_test = abap_true.
|
||||||
|
WRITE: / 'TESTLAUF - keine DB-Aenderung.'.
|
||||||
|
RETURN.
|
||||||
|
ENDIF.
|
||||||
|
|
||||||
|
DELETE FROM zprodsparte_map.
|
||||||
|
INSERT zprodsparte_map FROM TABLE lt_insert.
|
||||||
|
|
||||||
|
IF sy-subrc = 0.
|
||||||
|
COMMIT WORK.
|
||||||
|
WRITE: / lines( lt_insert ), 'Einzelwerte in ZPRODSPARTE_MAP geschrieben.'.
|
||||||
|
ELSE.
|
||||||
|
ROLLBACK WORK.
|
||||||
|
WRITE: / 'Fehler beim Schreiben, sy-subrc=', sy-subrc.
|
||||||
|
ENDIF.
|
||||||
Reference in New Issue
Block a user