diff --git a/src/EPPlus/Attributes/EpplusTableColumnAttributeBase.cs b/src/EPPlus/Attributes/EpplusTableColumnAttributeBase.cs index c3c86e7629..c1895936c4 100644 --- a/src/EPPlus/Attributes/EpplusTableColumnAttributeBase.cs +++ b/src/EPPlus/Attributes/EpplusTableColumnAttributeBase.cs @@ -24,14 +24,35 @@ namespace OfficeOpenXml.Attributes public abstract class EpplusTableColumnAttributeBase : Attribute { + private int _order = int.MaxValue; + private bool _orderIsSet = false; + /// /// Order of the columns value, default value is 0 /// public int Order { - get; - set; - } = int.MaxValue; + get + { + return _order; + } + set + { + _order = value; + _orderIsSet = true; + } + } + + /// + /// Returns true if the Order property has been explicitly set. + /// + internal bool OrderIsSet + { + get + { + return _orderIsSet; + } + } /// /// Name shown in the header row, overriding the property name diff --git a/src/EPPlus/LoadFunctions/LoadFromCollection.cs b/src/EPPlus/LoadFunctions/LoadFromCollection.cs index 5777a9090a..49be91997f 100644 --- a/src/EPPlus/LoadFunctions/LoadFromCollection.cs +++ b/src/EPPlus/LoadFunctions/LoadFromCollection.cs @@ -389,7 +389,7 @@ private string GetHeaderFromDotNetAttributes(MemberInfo member) var displayAttribute = member.GetFirstAttributeOfType(); if (displayAttribute != null) { - return displayAttribute.Name; + return displayAttribute.GetName(); } #endif return default; diff --git a/src/EPPlus/LoadFunctions/ReflectionHelpers/MemberPath.cs b/src/EPPlus/LoadFunctions/ReflectionHelpers/MemberPath.cs index a1e2548a2c..5247744745 100644 --- a/src/EPPlus/LoadFunctions/ReflectionHelpers/MemberPath.cs +++ b/src/EPPlus/LoadFunctions/ReflectionHelpers/MemberPath.cs @@ -114,7 +114,7 @@ public override string GetHeader() #if !NET35 else if (last.Member.HasAttributeOfType(out DisplayAttribute displayAttr)) { - header = displayAttr.Name; + header = displayAttr.GetName(); } #endif if (string.IsNullOrEmpty(header)) diff --git a/src/EPPlus/LoadFunctions/ReflectionHelpers/SortOrderExtensions.cs b/src/EPPlus/LoadFunctions/ReflectionHelpers/SortOrderExtensions.cs index d33c9fc19f..906c4b88e8 100644 --- a/src/EPPlus/LoadFunctions/ReflectionHelpers/SortOrderExtensions.cs +++ b/src/EPPlus/LoadFunctions/ReflectionHelpers/SortOrderExtensions.cs @@ -52,7 +52,7 @@ public static int GetSortOrder(this MemberInfo member, List filterMe { sortOrder = entcAttr.Order; } - else if(member.HasAttributeOfType(out EpplusTableColumnAttribute etcAttr)) + else if (member.HasAttributeOfType(out EpplusTableColumnAttribute etcAttr) && etcAttr.OrderIsSet) { sortOrder = etcAttr.Order; } diff --git a/src/EPPlusTest/EPPlus.Test.csproj b/src/EPPlusTest/EPPlus.Test.csproj index a1cdb5ed95..f0e6dced4e 100644 --- a/src/EPPlusTest/EPPlus.Test.csproj +++ b/src/EPPlusTest/EPPlus.Test.csproj @@ -50,6 +50,11 @@ + + True + True + LoadFromCollResources.resx + @@ -272,6 +277,12 @@ + + + PublicResXFileCodeGenerator + LoadFromCollResources.Designer.cs + + 3.1 diff --git a/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithDisplayOrderOnly.cs b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithDisplayOrderOnly.cs new file mode 100644 index 0000000000..947fee0f12 --- /dev/null +++ b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithDisplayOrderOnly.cs @@ -0,0 +1,29 @@ +/************************************************************************************************* + Required Notice: Copyright (C) EPPlus Software AB. + This software is licensed under PolyForm Noncommercial License 1.0.0 + and may only be used for noncommercial purposes + https://polyformproject.org/licenses/noncommercial/1.0.0/ + A commercial license to use this software can be purchased at https://epplussoftware.com + *************************************************************************************************/ +#if !NET35 +using System.ComponentModel.DataAnnotations; + +namespace EPPlusTest.LoadFunctions.AttributesTestClasses +{ + /// + /// Test class where only DisplayAttribute is present (no EpplusTableColumnAttribute). + /// DisplayAttribute.Order should be used in this case. + /// + public class ClassWithDisplayOrderOnly + { + [Display(Name = "Id Column", Order = 3)] + public int Id { get; set; } + + [Display(Name = "Name Column", Order = 1)] + public string Name { get; set; } + + [Display(Name = "Description Column", Order = 2)] + public string Description { get; set; } + } +} +#endif \ No newline at end of file diff --git a/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithDisplayResourceType.cs b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithDisplayResourceType.cs new file mode 100644 index 0000000000..3359d4bd87 --- /dev/null +++ b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithDisplayResourceType.cs @@ -0,0 +1,35 @@ +/************************************************************************************************* + Required Notice: Copyright (C) EPPlus Software AB. + This software is licensed under PolyForm Noncommercial License 1.0.0 + and may only be used for noncommercial purposes + https://polyformproject.org/licenses/noncommercial/1.0.0/ + + A commercial license to use this software can be purchased at https://epplussoftware.com + *************************************************************************************************/ +using OfficeOpenXml.Attributes; +#if !NET35 +using System.ComponentModel.DataAnnotations; + +namespace EPPlusTest.LoadFunctions.AttributesTestClasses +{ + /// + /// Test class using DisplayAttribute with ResourceType. + /// When ResourceType is set, GetName() should be used instead of Name + /// to get the localized value from the resource class. + /// + public class ClassWithDisplayResourceType + { + [EpplusTableColumn(Order = 1)] + [Display(Name = "IdHeader", ResourceType = typeof(LoadFromCollResources))] + public int Id { get; set; } + + [EpplusTableColumn(Order = 2)] + [Display(Name = "NameHeader", ResourceType = typeof(LoadFromCollResources))] + public string Name { get; set; } + + [EpplusTableColumn(Order = 3)] + [Display(Name = "DescriptionHeader", ResourceType = typeof(LoadFromCollResources))] + public string Description { get; set; } + } +} +#endif \ No newline at end of file diff --git a/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithDisplayResourceTypeOnly.cs b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithDisplayResourceTypeOnly.cs new file mode 100644 index 0000000000..bf77ddbf75 --- /dev/null +++ b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithDisplayResourceTypeOnly.cs @@ -0,0 +1,30 @@ +/************************************************************************************************* + Required Notice: Copyright (C) EPPlus Software AB. + This software is licensed under PolyForm Noncommercial License 1.0.0 + and may only be used for noncommercial purposes + https://polyformproject.org/licenses/noncommercial/1.0.0/ + + A commercial license to use this software can be purchased at https://epplussoftware.com + *************************************************************************************************/ +#if !NET35 +using System.ComponentModel.DataAnnotations; + +namespace EPPlusTest.LoadFunctions.AttributesTestClasses +{ + /// + /// Test class using DisplayAttribute with ResourceType but without EpplusTableColumnAttribute. + /// GetName() should return the localized value from the resource class. + /// + public class ClassWithDisplayResourceTypeOnly + { + [Display(Name = "IdHeader", ResourceType = typeof(LoadFromCollResources), Order = 1)] + public int Id { get; set; } + + [Display(Name = "NameHeader", ResourceType = typeof(LoadFromCollResources), Order = 2)] + public string Name { get; set; } + + [Display(Name = "DescriptionHeader", ResourceType = typeof(LoadFromCollResources), Order = 3)] + public string Description { get; set; } + } +} +#endif \ No newline at end of file diff --git a/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusHeaderAndDisplayName.cs b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusHeaderAndDisplayName.cs new file mode 100644 index 0000000000..c2b35d9209 --- /dev/null +++ b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusHeaderAndDisplayName.cs @@ -0,0 +1,30 @@ +/************************************************************************************************* + Required Notice: Copyright (C) EPPlus Software AB. + This software is licensed under PolyForm Noncommercial License 1.0.0 + and may only be used for noncommercial purposes + https://polyformproject.org/licenses/noncommercial/1.0.0/ + + A commercial license to use this software can be purchased at https://epplussoftware.com + *************************************************************************************************/ +using OfficeOpenXml.Attributes; +#if !NET35 +using System.ComponentModel.DataAnnotations; + +namespace EPPlusTest.LoadFunctions.AttributesTestClasses +{ + /// + /// Test class where EpplusTableColumnAttribute.Header IS set. + /// It should take precedence over DisplayAttribute.Name. + /// + public class ClassWithEpplusHeaderAndDisplayName + { + [EpplusTableColumn(Order = 1, Header = "EPPlus Id")] + [Display(Name = "Display Id")] + public int Id { get; set; } + + [EpplusTableColumn(Order = 2, Header = "EPPlus Name")] + [Display(Name = "Display Name")] + public string Name { get; set; } + } +} +#endif \ No newline at end of file diff --git a/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusNoOrderAndDisplayWithOrder.cs b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusNoOrderAndDisplayWithOrder.cs new file mode 100644 index 0000000000..ae38b75bb7 --- /dev/null +++ b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusNoOrderAndDisplayWithOrder.cs @@ -0,0 +1,29 @@ +using OfficeOpenXml.Attributes; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EPPlusTest.LoadFunctions.AttributesTestClasses +{ + /// + /// Test class where EpplusTableColumnAttribute exists but Order is NOT set. + /// Should fall back to DisplayAttribute.Order. + /// + public class ClassWithEpplusNoOrderAndDisplayWithOrder + { + [EpplusTableColumn(NumberFormat = "0")] + [Display(Name = "Id Column", Order = 3)] + public int Id { get; set; } + + [EpplusTableColumn] + [Display(Name = "Name Column", Order = 1)] + public string Name { get; set; } + + [EpplusTableColumn] + [Display(Name = "Description Column", Order = 2)] + public string Description { get; set; } + } +} diff --git a/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusOrderAndDisplayName.cs b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusOrderAndDisplayName.cs new file mode 100644 index 0000000000..13d294f45b --- /dev/null +++ b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusOrderAndDisplayName.cs @@ -0,0 +1,34 @@ +/************************************************************************************************* + Required Notice: Copyright (C) EPPlus Software AB. + This software is licensed under PolyForm Noncommercial License 1.0.0 + and may only be used for noncommercial purposes + https://polyformproject.org/licenses/noncommercial/1.0.0/ + + A commercial license to use this software can be purchased at https://epplussoftware.com + *************************************************************************************************/ +using OfficeOpenXml.Attributes; +#if !NET35 +using System.ComponentModel.DataAnnotations; + +namespace EPPlusTest.LoadFunctions.AttributesTestClasses +{ + /// + /// Test class where EpplusTableColumnAttribute has Order set but NOT Header. + /// Header should fall back to DisplayAttribute.GetName(). + /// + public class ClassWithEpplusOrderAndDisplayName + { + [EpplusTableColumn(Order = 3)] + [Display(Name = "The Id")] + public int Id { get; set; } + + [EpplusTableColumn(Order = 1)] + [Display(Name = "The Name")] + public string Name { get; set; } + + [EpplusTableColumn(Order = 2)] + [Display(Name = "The Description")] + public string Description { get; set; } + } +} +#endif \ No newline at end of file diff --git a/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusOrderAndDisplayWithoutOrder.cs b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusOrderAndDisplayWithoutOrder.cs new file mode 100644 index 0000000000..28c767fe3c --- /dev/null +++ b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusOrderAndDisplayWithoutOrder.cs @@ -0,0 +1,34 @@ +/************************************************************************************************* + Required Notice: Copyright (C) EPPlus Software AB. + This software is licensed under PolyForm Noncommercial License 1.0.0 + and may only be used for noncommercial purposes + https://polyformproject.org/licenses/noncommercial/1.0.0/ + A commercial license to use this software can be purchased at https://epplussoftware.com + *************************************************************************************************/ +using OfficeOpenXml.Attributes; +#if !NET35 +using System.ComponentModel.DataAnnotations; + +namespace EPPlusTest.LoadFunctions.AttributesTestClasses +{ + /// + /// Test class where EpplusTableColumnAttribute has Order set but + /// DisplayAttribute does NOT have Order set. + /// EpplusTableColumnAttribute.Order should be used. + /// + public class ClassWithEpplusOrderAndDisplayWithoutOrder + { + [EpplusTableColumn(Order = 3)] + [Display(Name = "Id Column")] + public int Id { get; set; } + + [EpplusTableColumn(Order = 1)] + [Display(Name = "Name Column")] + public string Name { get; set; } + + [EpplusTableColumn(Order = 2)] + [Display(Name = "Description Column")] + public string Description { get; set; } + } +} +#endif \ No newline at end of file diff --git a/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusTableColumnAndDisplayOrder.cs b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusTableColumnAndDisplayOrder.cs new file mode 100644 index 0000000000..a642e1afca --- /dev/null +++ b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithEpplusTableColumnAndDisplayOrder.cs @@ -0,0 +1,45 @@ +/************************************************************************************************* + Required Notice: Copyright (C) EPPlus Software AB. + This software is licensed under PolyForm Noncommercial License 1.0.0 + and may only be used for noncommercial purposes + https://polyformproject.org/licenses/noncommercial/1.0.0/ + + A commercial license to use this software can be purchased at https://epplussoftware.com + *************************************************************************************************/ +using OfficeOpenXml.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +#if !NET35 +using System.ComponentModel.DataAnnotations; +#endif + +namespace EPPlusTest.LoadFunctions.AttributesTestClasses +{ +#if !NET35 + /// + /// Test class where EpplusTableColumnAttribute.Order should take precedence + /// over DisplayAttribute.Order when both are present on the same property. + /// + public class ClassWithEpplusTableColumnAndDisplayOrder + { + // EpplusTableColumn Order = 3, Display Order = 1 + // Expected column order should be 3 (EpplusTableColumn wins) + [EpplusTableColumn(Order = 3)] + [Display(Name = "Id Column", Order = 1)] + public int Id { get; set; } + + // EpplusTableColumn Order = 1, Display Order = 3 + // Expected column order should be 1 (EpplusTableColumn wins) + [EpplusTableColumn(Order = 1)] + [Display(Name = "Name Column", Order = 3)] + public string Name { get; set; } + + // EpplusTableColumn Order = 2, Display Order = 2 + [EpplusTableColumn(Order = 2)] + [Display(Name = "Description Column", Order = 2)] + public string Description { get; set; } + } +#endif +} \ No newline at end of file diff --git a/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithNegativeEpplusOrder.cs b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithNegativeEpplusOrder.cs new file mode 100644 index 0000000000..9d8c3d6b3c --- /dev/null +++ b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/ClassWithNegativeEpplusOrder.cs @@ -0,0 +1,33 @@ +/************************************************************************************************* + Required Notice: Copyright (C) EPPlus Software AB. + This software is licensed under PolyForm Noncommercial License 1.0.0 + and may only be used for noncommercial purposes + https://polyformproject.org/licenses/noncommercial/1.0.0/ + A commercial license to use this software can be purchased at https://epplussoftware.com + *************************************************************************************************/ +using OfficeOpenXml.Attributes; +#if !NET35 +using System.ComponentModel.DataAnnotations; + +namespace EPPlusTest.LoadFunctions.AttributesTestClasses +{ + /// + /// Test class with a negative Order value on EpplusTableColumnAttribute, + /// matching the customer's scenario (Order = -90). + /// + public class ClassWithNegativeEpplusOrder + { + [EpplusTableColumn(Order = -90)] + [Display(Name = "NumRegistro", Order = 5)] + public int? NumRegistro { get; set; } + + [EpplusTableColumn(Order = 1)] + [Display(Name = "Nombre", Order = 1)] + public string Nombre { get; set; } + + [EpplusTableColumn(Order = 2)] + [Display(Name = "Descripcion", Order = 2)] + public string Descripcion { get; set; } + } +} +#endif \ No newline at end of file diff --git a/src/EPPlusTest/LoadFunctions/AttributesTestClasses/LoadFromCollResources.Designer.cs b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/LoadFromCollResources.Designer.cs new file mode 100644 index 0000000000..19fce98d98 --- /dev/null +++ b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/LoadFromCollResources.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace EPPlusTest.LoadFunctions.AttributesTestClasses { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class LoadFromCollResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal LoadFromCollResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("EPPlusTest.LoadFunctions.AttributesTestClasses.LoadFromCollResources", typeof(LoadFromCollResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Item Description. + /// + public static string DescriptionHeader { + get { + return ResourceManager.GetString("DescriptionHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Identifier. + /// + public static string IdHeader { + get { + return ResourceManager.GetString("IdHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Full Name. + /// + public static string NameHeader { + get { + return ResourceManager.GetString("NameHeader", resourceCulture); + } + } + } +} diff --git a/src/EPPlusTest/LoadFunctions/AttributesTestClasses/LoadFromCollResources.resx b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/LoadFromCollResources.resx new file mode 100644 index 0000000000..ccca8c2c1f --- /dev/null +++ b/src/EPPlusTest/LoadFunctions/AttributesTestClasses/LoadFromCollResources.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Item Description + + + Identifier + + + Full Name + + \ No newline at end of file diff --git a/src/EPPlusTest/LoadFunctions/LoadFromCollectionDisplayTests.cs b/src/EPPlusTest/LoadFunctions/LoadFromCollectionDisplayTests.cs new file mode 100644 index 0000000000..a3f6783b2c --- /dev/null +++ b/src/EPPlusTest/LoadFunctions/LoadFromCollectionDisplayTests.cs @@ -0,0 +1,292 @@ +/************************************************************************************************* + Required Notice: Copyright (C) EPPlus Software AB. + This software is licensed under PolyForm Noncommercial License 1.0.0 + and may only be used for noncommercial purposes + https://polyformproject.org/licenses/noncommercial/1.0.0/ + + A commercial license to use this software can be purchased at https://epplussoftware.com + *************************************************************************************************/ +#if !NET35 +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OfficeOpenXml; +using EPPlusTest.LoadFunctions.AttributesTestClasses; +using System.Collections.Generic; + +namespace EPPlusTest.LoadFunctions +{ + [TestClass] + public class LoadFromCollectionDisplayTests + { + private ExcelPackage _package; + private ExcelWorksheet _sheet; + + [TestInitialize] + public void Initialize() + { + _package = new ExcelPackage(); + _sheet = _package.Workbook.Worksheets.Add("test"); + } + + [TestCleanup] + public void Cleanup() + { + _package.Dispose(); + } + + #region Order precedence tests + + [TestMethod] + public void ShouldUseEpplusTableColumnOrderOverDisplayOrder() + { + // Arrange + // EpplusTableColumn Order: Id=3, Name=1, Description=2 + // Display Order: Id=1, Name=3, Description=2 + // Expected: EpplusTableColumn.Order wins + var items = new List + { + new ClassWithEpplusTableColumnAndDisplayOrder + { + Id = 1, + Name = "Test", + Description = "A test" + } + }; + + // Act + _sheet.Cells["A1"].LoadFromCollection(items, true); + + // Assert - column order should follow EpplusTableColumnAttribute.Order + Assert.AreEqual("Name Column", _sheet.Cells["A1"].Value, "First column should be Name (EpplusTableColumn Order=1)"); + Assert.AreEqual("Description Column", _sheet.Cells["B1"].Value, "Second column should be Description (EpplusTableColumn Order=2)"); + Assert.AreEqual("Id Column", _sheet.Cells["C1"].Value, "Third column should be Id (EpplusTableColumn Order=3)"); + } + + [TestMethod] + public void ShouldUseEpplusTableColumnOrderWithNegativeValues() + { + // Arrange - matches the customer scenario with Order = -90 + // EpplusTableColumn Order: NumRegistro=-90, Nombre=1, Descripcion=2 + // Display Order: NumRegistro=5, Nombre=1, Descripcion=2 + // Expected: EpplusTableColumn.Order wins, NumRegistro first + var items = new List + { + new ClassWithNegativeEpplusOrder + { + NumRegistro = 42, + Nombre = "Test", + Descripcion = "A test" + } + }; + + // Act + _sheet.Cells["A1"].LoadFromCollection(items, true); + + // Assert + Assert.AreEqual("NumRegistro", _sheet.Cells["A1"].Value, "First column should be NumRegistro (EpplusTableColumn Order=-90)"); + Assert.AreEqual("Nombre", _sheet.Cells["B1"].Value, "Second column should be Nombre (EpplusTableColumn Order=1)"); + Assert.AreEqual("Descripcion", _sheet.Cells["C1"].Value, "Third column should be Descripcion (EpplusTableColumn Order=2)"); + } + + [TestMethod] + public void ShouldUseEpplusTableColumnOrderWithNegativeValues_DataOrder() + { + // Arrange - verify that the data row also follows the correct column order + var items = new List + { + new ClassWithNegativeEpplusOrder + { + NumRegistro = 42, + Nombre = "Test", + Descripcion = "A test" + } + }; + + // Act + _sheet.Cells["A1"].LoadFromCollection(items, true); + + // Assert - row 2 is data + Assert.AreEqual(42, _sheet.Cells["A2"].Value, "First data column should be NumRegistro"); + Assert.AreEqual("Test", _sheet.Cells["B2"].Value, "Second data column should be Nombre"); + Assert.AreEqual("A test", _sheet.Cells["C2"].Value, "Third data column should be Descripcion"); + } + + [TestMethod] + public void ShouldFallBackToDisplayOrderWhenEpplusOrderNotSet() + { + // Arrange + // EpplusTableColumn exists but Order is NOT explicitly set + // Display Order: Id=3, Name=1, Description=2 + // Expected: falls back to DisplayAttribute.Order + var items = new List + { + new ClassWithEpplusNoOrderAndDisplayWithOrder + { + Id = 1, + Name = "Test", + Description = "A test" + } + }; + + // Act + _sheet.Cells["A1"].LoadFromCollection(items, true); + + // Assert - should follow DisplayAttribute.Order as fallback + Assert.AreEqual("Name Column", _sheet.Cells["A1"].Value, "First column should be Name (Display Order=1)"); + Assert.AreEqual("Description Column", _sheet.Cells["B1"].Value, "Second column should be Description (Display Order=2)"); + Assert.AreEqual("Id Column", _sheet.Cells["C1"].Value, "Third column should be Id (Display Order=3)"); + } + + [TestMethod] + public void ShouldUseDisplayOrderWhenNoEpplusTableColumnAttribute() + { + // Arrange - only DisplayAttribute present + // Display Order: Id=3, Name=1, Description=2 + var items = new List + { + new ClassWithDisplayOrderOnly + { + Id = 1, + Name = "Test", + Description = "A test" + } + }; + + // Act + _sheet.Cells["A1"].LoadFromCollection(items, true); + + // Assert + Assert.AreEqual("Name Column", _sheet.Cells["A1"].Value, "First column should be Name (Display Order=1)"); + Assert.AreEqual("Description Column", _sheet.Cells["B1"].Value, "Second column should be Description (Display Order=2)"); + Assert.AreEqual("Id Column", _sheet.Cells["C1"].Value, "Third column should be Id (Display Order=3)"); + } + + #endregion + + #region Header precedence tests + + [TestMethod] + public void ShouldUseEpplusHeaderOverDisplayName() + { + // Arrange - both EpplusTableColumn.Header and Display.Name are set + // Expected: EpplusTableColumn.Header wins + var items = new List + { + new ClassWithEpplusHeaderAndDisplayName + { + Id = 1, + Name = "Test" + } + }; + + // Act + _sheet.Cells["A1"].LoadFromCollection(items, true); + + // Assert + Assert.AreEqual("EPPlus Id", _sheet.Cells["A1"].Value, "Header should come from EpplusTableColumn.Header"); + Assert.AreEqual("EPPlus Name", _sheet.Cells["B1"].Value, "Header should come from EpplusTableColumn.Header"); + } + + [TestMethod] + public void ShouldFallBackToDisplayNameWhenEpplusHeaderNotSet() + { + // Arrange - EpplusTableColumn has Order but NOT Header + // Display has Name set + // Expected: falls back to DisplayAttribute.GetName() + var items = new List + { + new ClassWithEpplusOrderAndDisplayName + { + Id = 1, + Name = "Test", + Description = "A test" + } + }; + + // Act + _sheet.Cells["A1"].LoadFromCollection(items, true); + + // Assert - headers should come from Display.Name, order from EpplusTableColumn.Order + Assert.AreEqual("The Name", _sheet.Cells["A1"].Value, "Header should fall back to Display.Name"); + Assert.AreEqual("The Description", _sheet.Cells["B1"].Value, "Header should fall back to Display.Name"); + Assert.AreEqual("The Id", _sheet.Cells["C1"].Value, "Header should fall back to Display.Name"); + } + + [TestMethod] + public void ShouldUseDisplayNameAsHeaderWhenNoEpplusAttribute() + { + // Arrange - only DisplayAttribute present + var items = new List + { + new ClassWithDisplayOrderOnly + { + Id = 1, + Name = "Test", + Description = "A test" + } + }; + + // Act + _sheet.Cells["A1"].LoadFromCollection(items, true); + + // Assert + Assert.AreEqual("Name Column", _sheet.Cells["A1"].Value); + Assert.AreEqual("Description Column", _sheet.Cells["B1"].Value); + Assert.AreEqual("Id Column", _sheet.Cells["C1"].Value); + } + + [TestMethod] + public void ShouldUseGetNameForDisplayAttributeWithResourceType() + { + // Arrange - DisplayAttribute uses ResourceType for localization + // GetName() should return the localized value from the resource class, + // not the raw Name property (which is just the resource key). + // Display Name="IdHeader" with ResourceType=TestDisplayResources + // -> GetName() returns "Identifier" + // -> Name returns "IdHeader" + var items = new List + { + new ClassWithDisplayResourceType + { + Id = 1, + Name = "Test", + Description = "A test" + } + }; + + // Act + _sheet.Cells["A1"].LoadFromCollection(items, true); + + // Assert - headers should be the localized values from TestDisplayResources + Assert.AreEqual("Identifier", _sheet.Cells["A1"].Value, "Header should be localized value from resource class via GetName()"); + Assert.AreEqual("Full Name", _sheet.Cells["B1"].Value, "Header should be localized value from resource class via GetName()"); + Assert.AreEqual("Item Description", _sheet.Cells["C1"].Value, "Header should be localized value from resource class via GetName()"); + } + + [TestMethod] + public void ShouldUseGetNameForDisplayAttributeWithResourceType_NoEpplusAttribute() + { + // Arrange - same as above but verify the resource type pattern also works + // when no EpplusTableColumnAttribute is present (regression test) + var items = new List + { + new ClassWithDisplayResourceTypeOnly + { + Id = 1, + Name = "Test", + Description = "A test" + } + }; + + // Act + _sheet.Cells["A1"].LoadFromCollection(items, true); + + // Assert + Assert.AreEqual("Identifier", _sheet.Cells["A1"].Value, "Header should be localized value from resource class via GetName()"); + Assert.AreEqual("Full Name", _sheet.Cells["B1"].Value, "Header should be localized value from resource class via GetName()"); + Assert.AreEqual("Item Description", _sheet.Cells["C1"].Value, "Header should be localized value from resource class via GetName()"); + } + + #endregion + } +} +#endif \ No newline at end of file