using TrafagSalesExporter.Models; using TrafagSalesExporter.Services; namespace TrafagSalesExporter.Tests; public class MappedSalesRecordComposerTests { [Fact] public void Compose_MapsPrimarySourceConstantsAndODataDate() { var composer = new MappedSalesRecordComposer(); var site = new Site { TSC = "TRCH", Land = "Schweiz" }; var sources = new[] { new SapSourceDefinition { Alias = "Z", EntitySet = "ZSCHWEIZSet", IsPrimary = true, IsActive = true } }; var mappings = new[] { Mapping(nameof(SalesRecord.InvoiceNumber), "Z.VBELN"), Mapping(nameof(SalesRecord.PositionOnInvoice), "Z.POSNR"), Mapping(nameof(SalesRecord.InvoiceDate), "Z.FKDAT"), Mapping(nameof(SalesRecord.SalesPriceValue), "Z.NETWR_HC"), Mapping(nameof(SalesRecord.SalesCurrency), "Z.HWAER"), Mapping(nameof(SalesRecord.DocumentType), "=SAP") }; var rows = new Dictionary>>(StringComparer.OrdinalIgnoreCase) { ["Z"] = [ new(StringComparer.OrdinalIgnoreCase) { ["VBELN"] = "900001", ["POSNR"] = "10", ["FKDAT"] = "/Date(1735689600000)/", ["NETWR_HC"] = "1234.50", ["HWAER"] = "CHF" } ] }; var result = composer.Compose(site, sources, [], mappings, rows, "SAP"); Assert.Single(result); Assert.Equal("TRCH", result[0].Tsc); Assert.Equal("Schweiz", result[0].Land); Assert.Equal("900001", result[0].InvoiceNumber); Assert.Equal(10, result[0].PositionOnInvoice); Assert.Equal(new DateTime(2025, 1, 1), result[0].InvoiceDate); Assert.Equal(1234.50m, result[0].SalesPriceValue); Assert.Equal("CHF", result[0].SalesCurrency); Assert.Equal("SAP", result[0].DocumentType); } [Fact] public void Compose_AppliesLeftJoinAndDefaultDocumentType() { var composer = new MappedSalesRecordComposer(); var site = new Site { TSC = "TRAT", Land = "Oesterreich" }; var sources = new[] { new SapSourceDefinition { Alias = "H", EntitySet = "Header", IsPrimary = true, IsActive = true }, new SapSourceDefinition { Alias = "C", EntitySet = "Customer", IsActive = true, SortOrder = 1 } }; var joins = new[] { new SapJoinDefinition { LeftAlias = "H", RightAlias = "C", LeftKeys = "KUNNR", RightKeys = "KUNNR", IsActive = true } }; var mappings = new[] { Mapping(nameof(SalesRecord.InvoiceNumber), "H.VBELN"), Mapping(nameof(SalesRecord.CustomerName), "C.NAME1"), Mapping(nameof(SalesRecord.CustomerCountry), "C.LAND1") }; var rows = new Dictionary>>(StringComparer.OrdinalIgnoreCase) { ["H"] = [ new(StringComparer.OrdinalIgnoreCase) { ["VBELN"] = "910001", ["KUNNR"] = "1000" } ], ["C"] = [ new(StringComparer.OrdinalIgnoreCase) { ["KUNNR"] = "1000", ["NAME1"] = "Trafag AG", ["LAND1"] = "CH" } ] }; var result = composer.Compose(site, sources, joins, mappings, rows, "HANA"); Assert.Single(result); Assert.Equal("910001", result[0].InvoiceNumber); Assert.Equal("Trafag AG", result[0].CustomerName); Assert.Equal("CH", result[0].CustomerCountry); 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>>(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>>(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) => new() { TargetField = targetField, SourceExpression = sourceExpression, IsActive = true }; }