From 621dd41a970e0a13b11f5a8321ef0b3b465436b4 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 6 Jan 2026 23:56:02 -0500 Subject: [PATCH] refactor(ExcelIO): delete old attribute-based infrastructure - Remove OutputColumnAttribute, OutputTableAttribute, OutputColumnCache - Remove AttributeTableWriter and ColumnFormatter - Remove duplicate ExcelFormats from Mapping (use Formatting version) - Remove OutputColumn model - Add FilterEntryMaps for criteria sheet filter models - Update CriteriaSheetGenerator to use FluentTableWriter - Remove attributes from filter entry models (now use fluent maps) - Update DI to register filter entry maps and remove old services - Update tests to use new fluent infrastructure - Delete obsolete test files for removed infrastructure Task 16 of fluent-excel-mapping-implementation plan. --- .../Attributes/OutputColumnAttribute.cs | 58 ------ .../Attributes/OutputTableAttribute.cs | 23 --- .../JdeScoping.ExcelIO/DependencyInjection.cs | 18 +- .../Formatting/ColumnFormatter.cs | 54 ----- .../Generators/AttributeTableWriter.cs | 166 ---------------- .../Generators/CriteriaSheetGenerator.cs | 6 +- .../Helpers/OutputColumnCache.cs | 60 ------ .../Mapping/ExcelFormats.cs | 19 -- .../Mapping/Maps/FilterEntryMaps.cs | 119 +++++++++++ .../Maps/MisNonMatchSearchResultMap.cs | 5 +- .../Mapping/Maps/MisSearchResultMap.cs | 3 +- .../Mapping/Maps/SearchResultMap.cs | 5 +- .../JdeScoping.ExcelIO/Models/OutputColumn.cs | 25 --- .../Reporting/ComponentLotFilterEntry.cs | 5 - .../Models/Reporting/ItemNumberFilterEntry.cs | 5 - .../Reporting/ItemOperationMisFilterEntry.cs | 7 - .../Models/Reporting/OperatorFilterEntry.cs | 5 - .../Reporting/ProfitCenterFilterEntry.cs | 5 - .../Models/Reporting/TimespanFilter.cs | 5 - .../Models/Reporting/WorkCenterFilterEntry.cs | 5 - .../Models/Reporting/WorkOrderFilterEntry.cs | 5 - .../AttributeTableWriterTests.cs | 187 ------------------ .../ColumnFormatterTests.cs | 116 ----------- .../CriteriaSheetGeneratorTests.cs | 21 +- .../ExcelExportIntegrationTests.cs | 72 +++++-- .../ExcelExportServiceTests.cs | 35 +++- .../InclusionReasonTests.cs | 2 +- .../LegacyComparisonTests.cs | 37 +++- .../Mapping/ExcelClassMapTests.cs | 5 +- .../OutputColumnCacheTests.cs | 100 ---------- 30 files changed, 287 insertions(+), 891 deletions(-) delete mode 100644 NEW/src/JdeScoping.ExcelIO/Attributes/OutputColumnAttribute.cs delete mode 100644 NEW/src/JdeScoping.ExcelIO/Attributes/OutputTableAttribute.cs delete mode 100644 NEW/src/JdeScoping.ExcelIO/Formatting/ColumnFormatter.cs delete mode 100644 NEW/src/JdeScoping.ExcelIO/Generators/AttributeTableWriter.cs delete mode 100644 NEW/src/JdeScoping.ExcelIO/Helpers/OutputColumnCache.cs delete mode 100644 NEW/src/JdeScoping.ExcelIO/Mapping/ExcelFormats.cs create mode 100644 NEW/src/JdeScoping.ExcelIO/Mapping/Maps/FilterEntryMaps.cs delete mode 100644 NEW/src/JdeScoping.ExcelIO/Models/OutputColumn.cs delete mode 100644 NEW/tests/JdeScoping.ExcelIO.Tests/AttributeTableWriterTests.cs delete mode 100644 NEW/tests/JdeScoping.ExcelIO.Tests/ColumnFormatterTests.cs delete mode 100644 NEW/tests/JdeScoping.ExcelIO.Tests/OutputColumnCacheTests.cs diff --git a/NEW/src/JdeScoping.ExcelIO/Attributes/OutputColumnAttribute.cs b/NEW/src/JdeScoping.ExcelIO/Attributes/OutputColumnAttribute.cs deleted file mode 100644 index 0e81025..0000000 --- a/NEW/src/JdeScoping.ExcelIO/Attributes/OutputColumnAttribute.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace JdeScoping.ExcelIO.Attributes; - -/// -/// Excel output column specification. -/// -[AttributeUsage(AttributeTargets.Property)] -public class OutputColumnAttribute : Attribute -{ - /// - /// Standard format (text). - /// - public const string StdFormat = "@"; - - /// - /// Standard date format. - /// - public const string DateFormat = "[$-409]MM/dd/yyyy;@"; - - /// - /// Standard timestamp format. - /// - public const string TimestampFormat = "[$-409]m/d/yy h:mm AM/PM;@"; - - /// - /// Wrapped text column default width. - /// - public const double WrappedColumnWidth = 65; - - /// - /// Order to display column. - /// - public int Order { get; set; } - - /// - /// Override text to display for column header. - /// - public string HeaderText { get; set; } = string.Empty; - - /// - /// Column format (Excel formatting string). - /// - public string Format { get; set; } = StdFormat; - - /// - /// Whether or not width should be set automatically. - /// - public bool AutoWidth { get; set; } = true; - - /// - /// Manually set width (only used if AutoWidth = false). - /// - public double Width { get; set; } - - /// - /// Whether or not text should be wrapped. - /// - public bool WrapText { get; set; } -} diff --git a/NEW/src/JdeScoping.ExcelIO/Attributes/OutputTableAttribute.cs b/NEW/src/JdeScoping.ExcelIO/Attributes/OutputTableAttribute.cs deleted file mode 100644 index 92e5d81..0000000 --- a/NEW/src/JdeScoping.ExcelIO/Attributes/OutputTableAttribute.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace JdeScoping.ExcelIO.Attributes; - -/// -/// Excel output table specification. -/// -[AttributeUsage(AttributeTargets.Class)] -public class OutputTableAttribute : Attribute -{ - /// - /// Output tab name. - /// - public string TabName { get; set; } = string.Empty; - - /// - /// Table name. - /// - public string TableName { get; set; } = string.Empty; - - /// - /// Whether or not merged header should be shown. - /// - public bool ShowHeader { get; set; } -} diff --git a/NEW/src/JdeScoping.ExcelIO/DependencyInjection.cs b/NEW/src/JdeScoping.ExcelIO/DependencyInjection.cs index 4a107ad..f4e2ef1 100644 --- a/NEW/src/JdeScoping.ExcelIO/DependencyInjection.cs +++ b/NEW/src/JdeScoping.ExcelIO/DependencyInjection.cs @@ -2,7 +2,6 @@ using JdeScoping.Core.Interfaces; using JdeScoping.ExcelIO; using JdeScoping.ExcelIO.Options; using JdeScoping.ExcelIO.Generators; -using JdeScoping.ExcelIO.Helpers; using JdeScoping.ExcelIO.Mapping; using JdeScoping.ExcelIO.Mapping.Maps; using JdeScoping.ExcelIO.Parsing; @@ -42,18 +41,29 @@ public static class ExcelIODependencyInjection // Register generators (scoped - they use options) services.AddScoped(); - // Register helpers (singleton - stateless) - services.AddSingleton(); - services.AddSingleton(); + // Register template generator (singleton - stateless) services.AddSingleton(); // Register Excel map registry with all maps services.AddSingleton(sp => { var registry = new ExcelMapRegistry(); + + // Search result maps registry.Register(new SearchResultMap()); registry.Register(new MisSearchResultMap()); registry.Register(new MisNonMatchSearchResultMap()); + + // Filter entry maps (for criteria sheet) + registry.Register(new TimespanFilterMap()); + registry.Register(new WorkOrderFilterEntryMap()); + registry.Register(new ItemNumberFilterEntryMap()); + registry.Register(new ProfitCenterFilterEntryMap()); + registry.Register(new WorkCenterFilterEntryMap()); + registry.Register(new OperatorFilterEntryMap()); + registry.Register(new ComponentLotFilterEntryMap()); + registry.Register(new ItemOperationMisFilterEntryMap()); + return registry; }); diff --git a/NEW/src/JdeScoping.ExcelIO/Formatting/ColumnFormatter.cs b/NEW/src/JdeScoping.ExcelIO/Formatting/ColumnFormatter.cs deleted file mode 100644 index 6bcbd2b..0000000 --- a/NEW/src/JdeScoping.ExcelIO/Formatting/ColumnFormatter.cs +++ /dev/null @@ -1,54 +0,0 @@ -using ClosedXML.Excel; -using JdeScoping.ExcelIO.Attributes; - -namespace JdeScoping.ExcelIO.Formatting; - -/// -/// Column width and number format utilities. -/// -public static class ColumnFormatter -{ - /// - /// Applies column formatting based on attribute settings. - /// - /// The column to format. - /// The output column attribute with settings. - public static void ApplyColumnFormat(IXLColumn column, OutputColumnAttribute attr) - { - // Set number format - column.Style.NumberFormat.Format = attr.Format; - - // Handle wrap text - if (attr.WrapText) - { - column.Style.Alignment.WrapText = true; - } - - // Handle width - if (attr.WrapText && !attr.AutoWidth) - { - // Wrapped columns with fixed width skip auto-fit - column.Width = attr.Width; - } - else if (attr.AutoWidth) - { - column.AdjustToContents(); - column.Width *= ExcelFormats.DataPaddingFactor; - } - else - { - column.Width = attr.Width; - } - } - - /// - /// Auto-fits a column with the specified padding factor. - /// - /// The column to auto-fit. - /// The padding factor to apply (e.g., 1.15 for 15% padding). - public static void AutoFitWithPadding(IXLColumn column, double paddingFactor) - { - column.AdjustToContents(); - column.Width *= paddingFactor; - } -} diff --git a/NEW/src/JdeScoping.ExcelIO/Generators/AttributeTableWriter.cs b/NEW/src/JdeScoping.ExcelIO/Generators/AttributeTableWriter.cs deleted file mode 100644 index 3254ac2..0000000 --- a/NEW/src/JdeScoping.ExcelIO/Generators/AttributeTableWriter.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System.Reflection; -using ClosedXML.Excel; -using JdeScoping.ExcelIO.Attributes; -using JdeScoping.ExcelIO.Formatting; -using JdeScoping.ExcelIO.Helpers; - -namespace JdeScoping.ExcelIO.Generators; - -/// -/// Generic attribute-driven table writer for Excel worksheets. -/// -public class AttributeTableWriter -{ - private readonly OutputColumnCache _cache; - - /// - /// Initializes a new instance of the AttributeTableWriter class. - /// - /// The output column cache. - public AttributeTableWriter(OutputColumnCache cache) - { - _cache = cache; - } - - /// - /// Writes a table to the worksheet using attribute-driven column definitions. - /// - /// The type of data items. - /// The worksheet to write to. - /// The starting row (1-indexed). - /// The starting column (1-indexed). - /// The data to write. - /// Optional table name override. - /// Optional override for showing merged header. - /// Optional header text for merged header. - /// The created table, or null if no data. - public IXLTable? WriteTable( - IXLWorksheet worksheet, - int startRow, - int startCol, - IEnumerable data, - string? tableNameOverride = null, - bool? showHeader = null, - string? headerText = null) - { - var tableAttr = typeof(T).GetCustomAttribute(); - var columns = _cache.GetColumns(); - var tableName = tableNameOverride ?? tableAttr?.TableName ?? typeof(T).Name; - var shouldShowHeader = showHeader ?? (tableAttr?.ShowHeader ?? false); - var header = headerText ?? tableAttr?.TabName ?? string.Empty; - - if (columns.Count == 0) - { - return null; - } - - var dataList = data.ToList(); - var baseRow = startRow; - - // Write merged header if requested - if (shouldShowHeader && !string.IsNullOrEmpty(header)) - { - var mergedHeaderRange = worksheet.Range(baseRow, startCol, baseRow, startCol + columns.Count - 1); - HeaderFormatter.ApplyHeaderFormat(mergedHeaderRange, header, merge: true); - baseRow++; - } - - // Write column headers - var col = startCol; - foreach (var column in columns) - { - var cell = worksheet.Cell(baseRow, col); - HeaderFormatter.ApplyHeaderFormat(cell, column.Attribute.HeaderText); - - // Pre-set column formatting - worksheet.Column(col).Style.Alignment.WrapText = column.Attribute.WrapText; - if (!column.Attribute.AutoWidth) - { - worksheet.Column(col).Width = column.Attribute.Width; - } - - col++; - } - - // Write data rows - var row = baseRow + 1; - foreach (var item in dataList) - { - col = startCol; - foreach (var column in columns) - { - var value = column.Property.GetValue(item); - worksheet.Cell(row, col).Value = ConvertToXlValue(value); - col++; - } - row++; - } - - // Handle empty data case - add at least one empty row for valid table - if (dataList.Count == 0) - { - row = baseRow + 1; - } - - // Create table range - var dataRange = worksheet.Range( - baseRow, startCol, - baseRow + dataList.Count, startCol + columns.Count - 1); - - // Create table - var table = dataRange.CreateTable(tableName); - table.Theme = XLTableTheme.TableStyleLight18; - table.ShowTotalsRow = false; - - // Apply column formatting and number formats - col = startCol; - var tableStartRow = table.RangeAddress.FirstAddress.RowNumber; - var tableEndRow = table.RangeAddress.LastAddress.RowNumber; - - foreach (var column in columns) - { - // Apply number format to the column data - worksheet.Range(tableStartRow, col, tableEndRow, col) - .Style.NumberFormat.Format = column.Attribute.Format; - - // Apply column width - if (column.Attribute.WrapText && !column.Attribute.AutoWidth) - { - worksheet.Column(col).Width = column.Attribute.Width; - } - else if (column.Attribute.AutoWidth) - { - worksheet.Column(col).AdjustToContents(); - worksheet.Column(col).Width *= ExcelFormats.DataPaddingFactor; - } - else - { - worksheet.Column(col).Width = column.Attribute.Width; - } - - col++; - } - - return table; - } - - /// - /// Converts a value to an XLCellValue. - /// - private static XLCellValue ConvertToXlValue(object? value) - { - return value switch - { - null => Blank.Value, - string s => s, - int i => i, - long l => l, - decimal d => d, - double dbl => dbl, - float f => f, - DateTime dt => dt, - bool b => b, - _ => value.ToString() ?? string.Empty - }; - } -} diff --git a/NEW/src/JdeScoping.ExcelIO/Generators/CriteriaSheetGenerator.cs b/NEW/src/JdeScoping.ExcelIO/Generators/CriteriaSheetGenerator.cs index 2d1971d..ec4abbc 100644 --- a/NEW/src/JdeScoping.ExcelIO/Generators/CriteriaSheetGenerator.cs +++ b/NEW/src/JdeScoping.ExcelIO/Generators/CriteriaSheetGenerator.cs @@ -12,16 +12,16 @@ namespace JdeScoping.ExcelIO.Generators; public class CriteriaSheetGenerator { private readonly IOptions _options; - private readonly AttributeTableWriter _tableWriter; + private readonly FluentTableWriter _tableWriter; /// /// Initializes a new instance of the CriteriaSheetGenerator class. /// /// Excel export options. - /// Attribute table writer. + /// Fluent table writer. public CriteriaSheetGenerator( IOptions options, - AttributeTableWriter tableWriter) + FluentTableWriter tableWriter) { _options = options; _tableWriter = tableWriter; diff --git a/NEW/src/JdeScoping.ExcelIO/Helpers/OutputColumnCache.cs b/NEW/src/JdeScoping.ExcelIO/Helpers/OutputColumnCache.cs deleted file mode 100644 index 7c73e63..0000000 --- a/NEW/src/JdeScoping.ExcelIO/Helpers/OutputColumnCache.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Collections.Concurrent; -using System.Reflection; -using JdeScoping.ExcelIO.Attributes; -using JdeScoping.ExcelIO.Models; - -namespace JdeScoping.ExcelIO.Helpers; - -/// -/// Cached reflection for column metadata. -/// -public class OutputColumnCache -{ - private readonly ConcurrentDictionary> _cache = new(); - - /// - /// Gets the output columns for a given type. - /// - /// The type to get columns for. - /// Ordered list of output columns. - public IReadOnlyList GetColumns() - { - return GetColumns(typeof(T)); - } - - /// - /// Gets the output columns for a given type. - /// - /// The type to get columns for. - /// Ordered list of output columns. - public IReadOnlyList GetColumns(Type type) - { - return _cache.GetOrAdd(type, BuildColumns); - } - - private static IReadOnlyList BuildColumns(Type type) - { - var columns = new List(); - - foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) - { - var attr = property.GetCustomAttribute(); - if (attr != null) - { - columns.Add(new OutputColumn - { - Name = property.Name, - Property = property, - Attribute = attr - }); - } - } - - // Sort by Order ascending, then by Name alphabetically for tie-breaking - return columns - .OrderBy(c => c.Attribute.Order) - .ThenBy(c => c.Name) - .ToList() - .AsReadOnly(); - } -} diff --git a/NEW/src/JdeScoping.ExcelIO/Mapping/ExcelFormats.cs b/NEW/src/JdeScoping.ExcelIO/Mapping/ExcelFormats.cs deleted file mode 100644 index f35e762..0000000 --- a/NEW/src/JdeScoping.ExcelIO/Mapping/ExcelFormats.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace JdeScoping.ExcelIO.Mapping; - -/// -/// Standard Excel format strings for column configuration. -/// -public static class ExcelFormats -{ - /// Text format (default). - public const string Text = "@"; - - /// Date format: MM/dd/yyyy - public const string Date = "[$-409]MM/dd/yyyy;@"; - - /// Timestamp format: m/d/yy h:mm AM/PM - public const string Timestamp = "[$-409]m/d/yy h:mm AM/PM;@"; - - /// Default width for wrapped text columns. - public const double WrappedColumnWidth = 65; -} diff --git a/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/FilterEntryMaps.cs b/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/FilterEntryMaps.cs new file mode 100644 index 0000000..cc5db1a --- /dev/null +++ b/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/FilterEntryMaps.cs @@ -0,0 +1,119 @@ +using JdeScoping.ExcelIO.Formatting; +using JdeScoping.ExcelIO.Models.Reporting; + +namespace JdeScoping.ExcelIO.Mapping.Maps; + +/// +/// Excel column mapping for TimespanFilter. +/// +public sealed class TimespanFilterMap : ExcelClassMap +{ + public TimespanFilterMap() + { + Table("Timespan_Filter", "Timespan Filter"); + + Map(x => x.MinimumDt).Order(10).Header("Minimum Date").Format(ExcelFormats.DateFormat); + Map(x => x.MaximumDt).Order(20).Header("Maximum Date").Format(ExcelFormats.DateFormat); + } +} + +/// +/// Excel column mapping for WorkOrderFilterEntry. +/// +public sealed class WorkOrderFilterEntryMap : ExcelClassMap +{ + public WorkOrderFilterEntryMap() + { + Table("Work_Order_Filter", "Work Order Filter"); + + Map(x => x.WorkOrderNumber).Order(10).Header("Work Order Number"); + Map(x => x.ItemNumber).Order(20).Header("Item Number"); + } +} + +/// +/// Excel column mapping for ItemNumberFilterEntry. +/// +public sealed class ItemNumberFilterEntryMap : ExcelClassMap +{ + public ItemNumberFilterEntryMap() + { + Table("Item_Number_Filter", "Item Number Filter"); + + Map(x => x.ItemNumber).Order(10).Header("Item Number"); + Map(x => x.ItemDescription).Order(20).Header("Item Description"); + } +} + +/// +/// Excel column mapping for ProfitCenterFilterEntry. +/// +public sealed class ProfitCenterFilterEntryMap : ExcelClassMap +{ + public ProfitCenterFilterEntryMap() + { + Table("Profit_Center_Filter", "Profit Center Filter"); + + Map(x => x.Code).Order(10).Header("Profit Center"); + Map(x => x.Description).Order(20).Header("Description"); + } +} + +/// +/// Excel column mapping for WorkCenterFilterEntry. +/// +public sealed class WorkCenterFilterEntryMap : ExcelClassMap +{ + public WorkCenterFilterEntryMap() + { + Table("Work_Center_Filter", "Work Center Filter"); + + Map(x => x.Code).Order(10).Header("Work Center"); + Map(x => x.Description).Order(20).Header("Description"); + } +} + +/// +/// Excel column mapping for OperatorFilterEntry. +/// +public sealed class OperatorFilterEntryMap : ExcelClassMap +{ + public OperatorFilterEntryMap() + { + Table("Operator_Filter", "Operator Filter"); + + // Note: AddressNumber is not exported to Excel (no OutputColumn attribute in original) + Map(x => x.UserId).Order(10).Header("Username"); + Map(x => x.FullName).Order(20).Header("Name"); + } +} + +/// +/// Excel column mapping for ComponentLotFilterEntry. +/// +public sealed class ComponentLotFilterEntryMap : ExcelClassMap +{ + public ComponentLotFilterEntryMap() + { + Table("Component_Lot_Filter", "Component Lot Filter"); + + Map(x => x.LotNumber).Order(10).Header("Lot Number"); + Map(x => x.ItemNumber).Order(20).Header("Item Number"); + } +} + +/// +/// Excel column mapping for ItemOperationMisFilterEntry. +/// +public sealed class ItemOperationMisFilterEntryMap : ExcelClassMap +{ + public ItemOperationMisFilterEntryMap() + { + Table("Item_Operation_MIS_Filter", "Item/Operation/MIS Filter"); + + Map(x => x.ItemNumber).Order(10).Header("Item Number"); + Map(x => x.OperationNumber).Order(20).Header("Operation Number"); + Map(x => x.MisNumber).Order(30).Header("MIS Number"); + Map(x => x.MisRevision).Order(40).Header("MIS Revision"); + } +} diff --git a/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/MisNonMatchSearchResultMap.cs b/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/MisNonMatchSearchResultMap.cs index 63c18f3..66ef306 100644 --- a/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/MisNonMatchSearchResultMap.cs +++ b/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/MisNonMatchSearchResultMap.cs @@ -1,4 +1,5 @@ using JdeScoping.Core.Models.SearchResults; +using JdeScoping.ExcelIO.Formatting; namespace JdeScoping.ExcelIO.Mapping.Maps; @@ -13,10 +14,10 @@ public sealed class MisNonMatchSearchResultMap : ExcelClassMap x.WorkCenterCode).Order(10).Header("Work Center Code"); Map(x => x.WorkOrderNumber).Order(20).Header("Work Order Number"); - Map(x => x.WorkOrderStartDate).Order(30).Header("Work Order Start Date").Format(ExcelFormats.Date); + Map(x => x.WorkOrderStartDate).Order(30).Header("Work Order Start Date").Format(ExcelFormats.DateFormat); Map(x => x.JobStepNumber).Order(40).Header("Job Step Number"); Map(x => x.JobStepDescription).Order(50).Header("Function Operation Description"); - Map(x => x.JobStepEndDate).Order(60).Header("Job Step End Date").Format(ExcelFormats.Date); + Map(x => x.JobStepEndDate).Order(60).Header("Job Step End Date").Format(ExcelFormats.DateFormat); Map(x => x.FunctionCode).Order(70).Header("Function Code"); Map(x => x.WasJobStepAdded).Order(75).Header("Was Job Step Added?"); Map(x => x.MatchedJobStepNumber).Order(76).Header("Matched Job Step Number"); diff --git a/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/MisSearchResultMap.cs b/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/MisSearchResultMap.cs index e82d79d..857d7fe 100644 --- a/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/MisSearchResultMap.cs +++ b/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/MisSearchResultMap.cs @@ -1,4 +1,5 @@ using JdeScoping.Core.Models.SearchResults; +using JdeScoping.ExcelIO.Formatting; namespace JdeScoping.ExcelIO.Mapping.Maps; @@ -17,7 +18,7 @@ public sealed class MisSearchResultMap : ExcelClassMap Map(x => x.RevId).Order(40).Header("MIS Revision"); Map(x => x.ItemDescription).Order(50).Header("Item Description"); Map(x => x.Status).Order(60).Header("MIS Release Status"); - Map(x => x.ReleaseDate).Order(70).Header("MIS Release Date").Format(ExcelFormats.Timestamp); + Map(x => x.ReleaseDate).Order(70).Header("MIS Release Date").Format(ExcelFormats.TimestampFormat); Map(x => x.BranchCode).Order(80).Header("Branch Code"); Map(x => x.JobStepSequenceNumber).Order(90).Header("Job Step Sequence Number"); Map(x => x.MatchedSequenceNumber).Order(100).Header("Matched Sequence Number"); diff --git a/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/SearchResultMap.cs b/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/SearchResultMap.cs index 42daadc..12ba329 100644 --- a/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/SearchResultMap.cs +++ b/NEW/src/JdeScoping.ExcelIO/Mapping/Maps/SearchResultMap.cs @@ -1,4 +1,5 @@ using JdeScoping.Core.Models.SearchResults; +using JdeScoping.ExcelIO.Formatting; namespace JdeScoping.ExcelIO.Mapping.Maps; @@ -25,10 +26,10 @@ public sealed class SearchResultMap : ExcelClassMap Map(x => x.StepNumber).Order(110).Header("Operation Step"); Map(x => x.StepDescription).Order(120).Header("Operation Step Description"); Map(x => x.FunctionOperationDescription).Order(130).Header("Function Operation Description"); - Map(x => x.StepUpdateDt).Order(140).Header("Operation Step Update Timestamp").Format(ExcelFormats.Timestamp); + Map(x => x.StepUpdateDt).Order(140).Header("Operation Step Update Timestamp").Format(ExcelFormats.TimestampFormat); Map(x => x.StatusCode).Order(150).Header("Status Code"); Map(x => x.StatusDescription).Order(160).Header("Status Description"); - Map(x => x.StatusUpdateDt).Order(170).Header("Status Update Timestamp").Format(ExcelFormats.Date); + Map(x => x.StatusUpdateDt).Order(170).Header("Status Update Timestamp").Format(ExcelFormats.DateFormat); Map(x => x.InclusionReason).Order(180).Header("Inclusion Reason"); } } diff --git a/NEW/src/JdeScoping.ExcelIO/Models/OutputColumn.cs b/NEW/src/JdeScoping.ExcelIO/Models/OutputColumn.cs deleted file mode 100644 index ada8a92..0000000 --- a/NEW/src/JdeScoping.ExcelIO/Models/OutputColumn.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Reflection; -using JdeScoping.ExcelIO.Attributes; - -namespace JdeScoping.ExcelIO.Models; - -/// -/// Column metadata model for Excel output. -/// -public class OutputColumn -{ - /// - /// Property name. - /// - public string Name { get; init; } = string.Empty; - - /// - /// Property info for reflection. - /// - public PropertyInfo Property { get; init; } = null!; - - /// - /// Output column attribute. - /// - public OutputColumnAttribute Attribute { get; init; } = null!; -} diff --git a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ComponentLotFilterEntry.cs b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ComponentLotFilterEntry.cs index 6b92149..bed0e7e 100644 --- a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ComponentLotFilterEntry.cs +++ b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ComponentLotFilterEntry.cs @@ -1,22 +1,17 @@ -using JdeScoping.ExcelIO.Attributes; - namespace JdeScoping.ExcelIO.Models.Reporting; /// /// Component lot search filter entry. /// -[OutputTable(TabName = "Component Lot Filter", ShowHeader = true, TableName = "Component_Lot_Filter")] public class ComponentLotFilterEntry { /// /// Component lot number. /// - [OutputColumn(Order = 10, HeaderText = "Lot Number")] public string LotNumber { get; set; } = string.Empty; /// /// Component lot item number. /// - [OutputColumn(Order = 20, HeaderText = "Item Number")] public string ItemNumber { get; set; } = string.Empty; } diff --git a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ItemNumberFilterEntry.cs b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ItemNumberFilterEntry.cs index 43d359d..c644420 100644 --- a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ItemNumberFilterEntry.cs +++ b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ItemNumberFilterEntry.cs @@ -1,22 +1,17 @@ -using JdeScoping.ExcelIO.Attributes; - namespace JdeScoping.ExcelIO.Models.Reporting; /// /// Item number search filter entry. /// -[OutputTable(TabName = "Item Number Filter", ShowHeader = true, TableName = "Item_Number_Filter")] public class ItemNumberFilterEntry { /// /// Item number. /// - [OutputColumn(Order = 10, HeaderText = "Item Number")] public string ItemNumber { get; set; } = string.Empty; /// /// Item description. /// - [OutputColumn(Order = 20, HeaderText = "Item Description")] public string ItemDescription { get; set; } = string.Empty; } diff --git a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ItemOperationMisFilterEntry.cs b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ItemOperationMisFilterEntry.cs index 46323b9..6c5c79d 100644 --- a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ItemOperationMisFilterEntry.cs +++ b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ItemOperationMisFilterEntry.cs @@ -1,34 +1,27 @@ -using JdeScoping.ExcelIO.Attributes; - namespace JdeScoping.ExcelIO.Models.Reporting; /// /// Item/Operation/MIS search filter entry. /// -[OutputTable(TabName = "Item/Operation/MIS Filter", ShowHeader = true, TableName = "Item_Operation_MIS_Filter")] public class ItemOperationMisFilterEntry { /// /// Part's item number. /// - [OutputColumn(Order = 10, HeaderText = "Item Number")] public string ItemNumber { get; set; } = string.Empty; /// /// Operation's job step number. /// - [OutputColumn(Order = 20, HeaderText = "Operation Number")] public string OperationNumber { get; set; } = string.Empty; /// /// MIS number. /// - [OutputColumn(Order = 30, HeaderText = "MIS Number")] public string MisNumber { get; set; } = string.Empty; /// /// MIS revision. /// - [OutputColumn(Order = 40, HeaderText = "MIS Revision")] public string MisRevision { get; set; } = string.Empty; } diff --git a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/OperatorFilterEntry.cs b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/OperatorFilterEntry.cs index 14f779c..665d91a 100644 --- a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/OperatorFilterEntry.cs +++ b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/OperatorFilterEntry.cs @@ -1,11 +1,8 @@ -using JdeScoping.ExcelIO.Attributes; - namespace JdeScoping.ExcelIO.Models.Reporting; /// /// Operator search filter entry. /// -[OutputTable(TabName = "Operator Filter", ShowHeader = true, TableName = "Operator_Filter")] public class OperatorFilterEntry { /// @@ -16,12 +13,10 @@ public class OperatorFilterEntry /// /// Operator login user ID. /// - [OutputColumn(Order = 10, HeaderText = "Username")] public string UserId { get; set; } = string.Empty; /// /// Operator full name (FIRST + LAST). /// - [OutputColumn(Order = 20, HeaderText = "Name")] public string FullName { get; set; } = string.Empty; } diff --git a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ProfitCenterFilterEntry.cs b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ProfitCenterFilterEntry.cs index 86bdeb6..29144a0 100644 --- a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ProfitCenterFilterEntry.cs +++ b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/ProfitCenterFilterEntry.cs @@ -1,22 +1,17 @@ -using JdeScoping.ExcelIO.Attributes; - namespace JdeScoping.ExcelIO.Models.Reporting; /// /// Profit center search filter entry. /// -[OutputTable(TabName = "Profit Center Filter", ShowHeader = true, TableName = "Profit_Center_Filter")] public class ProfitCenterFilterEntry { /// /// Profit center code. /// - [OutputColumn(Order = 10, HeaderText = "Profit Center")] public string Code { get; set; } = string.Empty; /// /// Profit center description. /// - [OutputColumn(Order = 20, HeaderText = "Description")] public string Description { get; set; } = string.Empty; } diff --git a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/TimespanFilter.cs b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/TimespanFilter.cs index c79d569..262f0d9 100644 --- a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/TimespanFilter.cs +++ b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/TimespanFilter.cs @@ -1,22 +1,17 @@ -using JdeScoping.ExcelIO.Attributes; - namespace JdeScoping.ExcelIO.Models.Reporting; /// /// Timespan filter entry for criteria sheet. /// -[OutputTable(TabName = "Timespan Filter", ShowHeader = true, TableName = "Timespan_Filter")] public class TimespanFilter { /// /// Minimum date/time. /// - [OutputColumn(Order = 10, HeaderText = "Minimum Date", Format = OutputColumnAttribute.DateFormat)] public DateTime? MinimumDt { get; set; } /// /// Maximum date/time. /// - [OutputColumn(Order = 20, HeaderText = "Maximum Date", Format = OutputColumnAttribute.DateFormat)] public DateTime? MaximumDt { get; set; } } diff --git a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/WorkCenterFilterEntry.cs b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/WorkCenterFilterEntry.cs index 539460a..056c716 100644 --- a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/WorkCenterFilterEntry.cs +++ b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/WorkCenterFilterEntry.cs @@ -1,22 +1,17 @@ -using JdeScoping.ExcelIO.Attributes; - namespace JdeScoping.ExcelIO.Models.Reporting; /// /// Work center search filter entry. /// -[OutputTable(TabName = "Work Center Filter", ShowHeader = true, TableName = "Work_Center_Filter")] public class WorkCenterFilterEntry { /// /// Work center code. /// - [OutputColumn(Order = 10, HeaderText = "Work Center")] public string Code { get; set; } = string.Empty; /// /// Work center description. /// - [OutputColumn(Order = 20, HeaderText = "Description")] public string Description { get; set; } = string.Empty; } diff --git a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/WorkOrderFilterEntry.cs b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/WorkOrderFilterEntry.cs index 9be7c41..0f79c7c 100644 --- a/NEW/src/JdeScoping.ExcelIO/Models/Reporting/WorkOrderFilterEntry.cs +++ b/NEW/src/JdeScoping.ExcelIO/Models/Reporting/WorkOrderFilterEntry.cs @@ -1,22 +1,17 @@ -using JdeScoping.ExcelIO.Attributes; - namespace JdeScoping.ExcelIO.Models.Reporting; /// /// Work order search filter entry. /// -[OutputTable(TabName = "Work Order Filter", ShowHeader = true, TableName = "Work_Order_Filter")] public class WorkOrderFilterEntry { /// /// Work order number. /// - [OutputColumn(Order = 10, HeaderText = "Work Order Number")] public long WorkOrderNumber { get; set; } /// /// Work order item number. /// - [OutputColumn(Order = 20, HeaderText = "Item Number")] public string ItemNumber { get; set; } = string.Empty; } diff --git a/NEW/tests/JdeScoping.ExcelIO.Tests/AttributeTableWriterTests.cs b/NEW/tests/JdeScoping.ExcelIO.Tests/AttributeTableWriterTests.cs deleted file mode 100644 index 2ef74c9..0000000 --- a/NEW/tests/JdeScoping.ExcelIO.Tests/AttributeTableWriterTests.cs +++ /dev/null @@ -1,187 +0,0 @@ -using ClosedXML.Excel; -using JdeScoping.ExcelIO.Attributes; -using JdeScoping.ExcelIO.Generators; -using JdeScoping.ExcelIO.Helpers; -using Shouldly; -using Xunit; - -namespace JdeScoping.ExcelIO.Tests; - -public class AttributeTableWriterTests -{ - private readonly OutputColumnCache _cache = new(); - private readonly AttributeTableWriter _writer; - - public AttributeTableWriterTests() - { - _writer = new AttributeTableWriter(_cache); - } - - [OutputTable(TabName = "Test Items", TableName = "Test_Items", ShowHeader = false)] - private class TestItem - { - [OutputColumn(Order = 10, HeaderText = "ID")] - public int Id { get; set; } - - [OutputColumn(Order = 20, HeaderText = "Name")] - public string Name { get; set; } = string.Empty; - - [OutputColumn(Order = 30, HeaderText = "Value")] - public decimal Value { get; set; } - } - - [OutputTable(TabName = "Wrapped Table", TableName = "Wrapped_Table")] - private class WrappedItem - { - [OutputColumn(Order = 10, HeaderText = "Description", WrapText = true, AutoWidth = false, Width = 65)] - public string Description { get; set; } = string.Empty; - } - - private class NoAttributeItem - { - public string Data { get; set; } = string.Empty; - } - - [Fact] - public void WriteTable_CreatesTableWithCorrectColumns() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var data = new List - { - new() { Id = 1, Name = "Item 1", Value = 10.5m }, - new() { Id = 2, Name = "Item 2", Value = 20.5m } - }; - - var table = _writer.WriteTable(worksheet, 1, 1, data); - - table.ShouldNotBeNull(); - table.ColumnCount().ShouldBe(3); - table.RowCount().ShouldBe(3); // Header + 2 data rows - } - - [Fact] - public void WriteTable_UsesLight18TableStyle() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var data = new List { new() { Id = 1, Name = "Test", Value = 100m } }; - - var table = _writer.WriteTable(worksheet, 1, 1, data); - - table.ShouldNotBeNull(); - table.Theme.ShouldBe(XLTableTheme.TableStyleLight18); - } - - [Fact] - public void WriteTable_SetsColumnHeaders() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var data = new List { new() { Id = 1, Name = "Test", Value = 100m } }; - - _writer.WriteTable(worksheet, 1, 1, data); - - worksheet.Cell(1, 1).Value.GetText().ShouldBe("ID"); - worksheet.Cell(1, 2).Value.GetText().ShouldBe("Name"); - worksheet.Cell(1, 3).Value.GetText().ShouldBe("Value"); - } - - [Fact] - public void WriteTable_WritesDataRows() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var data = new List - { - new() { Id = 1, Name = "Item 1", Value = 10.5m }, - new() { Id = 2, Name = "Item 2", Value = 20.5m } - }; - - _writer.WriteTable(worksheet, 1, 1, data); - - worksheet.Cell(2, 1).Value.GetNumber().ShouldBe(1); - worksheet.Cell(2, 2).Value.GetText().ShouldBe("Item 1"); - worksheet.Cell(2, 3).Value.GetNumber().ShouldBe(10.5); - worksheet.Cell(3, 1).Value.GetNumber().ShouldBe(2); - } - - [Fact] - public void WriteTable_WithShowHeader_CreatesMergedHeader() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var data = new List { new() { Id = 1, Name = "Test", Value = 100m } }; - - _writer.WriteTable(worksheet, 1, 1, data, showHeader: true, headerText: "Test Header"); - - // First row should be merged header - var headerRange = worksheet.Range(1, 1, 1, 3); - headerRange.IsMerged().ShouldBeTrue(); - worksheet.Cell(1, 1).Value.GetText().ShouldBe("Test Header"); - - // Column headers should be on row 2 - worksheet.Cell(2, 1).Value.GetText().ShouldBe("ID"); - } - - [Fact] - public void WriteTable_EmptyData_CreatesTableWithHeaderOnly() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var data = new List(); - - var table = _writer.WriteTable(worksheet, 1, 1, data); - - table.ShouldNotBeNull(); - // Table should exist with headers - table.ColumnCount().ShouldBe(3); - } - - [Fact] - public void WriteTable_NoAttributes_ReturnsNull() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var data = new List { new() { Data = "Test" } }; - - var table = _writer.WriteTable(worksheet, 1, 1, data); - - table.ShouldBeNull(); - } - - [Fact] - public void WriteTable_WrappedColumn_SetsFixedWidth() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var data = new List { new() { Description = "Long description text" } }; - - _writer.WriteTable(worksheet, 1, 1, data); - - worksheet.Column(1).Width.ShouldBe(65); - worksheet.Column(1).Style.Alignment.WrapText.ShouldBeTrue(); - } - - [Fact] - public void WriteTable_TableNameOverride_UsesProvidedName() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var data = new List { new() { Id = 1, Name = "Test", Value = 100m } }; - - var table = _writer.WriteTable(worksheet, 1, 1, data, tableNameOverride: "Custom_Table"); - - table.ShouldNotBeNull(); - table.Name.ShouldBe("Custom_Table"); - } -} diff --git a/NEW/tests/JdeScoping.ExcelIO.Tests/ColumnFormatterTests.cs b/NEW/tests/JdeScoping.ExcelIO.Tests/ColumnFormatterTests.cs deleted file mode 100644 index b830c90..0000000 --- a/NEW/tests/JdeScoping.ExcelIO.Tests/ColumnFormatterTests.cs +++ /dev/null @@ -1,116 +0,0 @@ -using ClosedXML.Excel; -using JdeScoping.ExcelIO.Attributes; -using JdeScoping.ExcelIO.Formatting; -using Shouldly; -using Xunit; - -namespace JdeScoping.ExcelIO.Tests; - -public class ColumnFormatterTests -{ - [Fact] - public void ApplyColumnFormat_AutoWidth_AdjustsToContents() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - worksheet.Cell(1, 1).Value = "Some Text Value"; - - var attr = new OutputColumnAttribute - { - AutoWidth = true, - Format = OutputColumnAttribute.StdFormat - }; - - ColumnFormatter.ApplyColumnFormat(worksheet.Column(1), attr); - - // Width should be greater than default after adjustment - worksheet.Column(1).Width.ShouldBeGreaterThan(0); - } - - [Fact] - public void ApplyColumnFormat_FixedWidth_SetsExactWidth() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var attr = new OutputColumnAttribute - { - AutoWidth = false, - Width = 50.0, - Format = OutputColumnAttribute.StdFormat - }; - - ColumnFormatter.ApplyColumnFormat(worksheet.Column(1), attr); - - worksheet.Column(1).Width.ShouldBe(50.0); - } - - [Fact] - public void ApplyColumnFormat_WrapText_EnablesWrapping() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var attr = new OutputColumnAttribute - { - WrapText = true, - AutoWidth = false, - Width = 65.0, - Format = OutputColumnAttribute.StdFormat - }; - - ColumnFormatter.ApplyColumnFormat(worksheet.Column(1), attr); - - worksheet.Column(1).Style.Alignment.WrapText.ShouldBeTrue(); - worksheet.Column(1).Width.ShouldBe(65.0); - } - - [Fact] - public void ApplyColumnFormat_DateFormat_AppliesCorrectFormat() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var attr = new OutputColumnAttribute - { - AutoWidth = false, - Width = 20.0, - Format = OutputColumnAttribute.DateFormat - }; - - ColumnFormatter.ApplyColumnFormat(worksheet.Column(1), attr); - - worksheet.Column(1).Style.NumberFormat.Format.ShouldBe(OutputColumnAttribute.DateFormat); - } - - [Fact] - public void ApplyColumnFormat_TimestampFormat_AppliesCorrectFormat() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - - var attr = new OutputColumnAttribute - { - AutoWidth = false, - Width = 25.0, - Format = OutputColumnAttribute.TimestampFormat - }; - - ColumnFormatter.ApplyColumnFormat(worksheet.Column(1), attr); - - worksheet.Column(1).Style.NumberFormat.Format.ShouldBe(OutputColumnAttribute.TimestampFormat); - } - - [Fact] - public void AutoFitWithPadding_AppliesPaddingFactor() - { - using var workbook = new XLWorkbook(); - var worksheet = workbook.Worksheets.Add("Test"); - worksheet.Cell(1, 1).Value = "Some Text"; - - ColumnFormatter.AutoFitWithPadding(worksheet.Column(1), 1.30); - - // Width should be greater than 0 and include padding - worksheet.Column(1).Width.ShouldBeGreaterThan(0); - } -} diff --git a/NEW/tests/JdeScoping.ExcelIO.Tests/CriteriaSheetGeneratorTests.cs b/NEW/tests/JdeScoping.ExcelIO.Tests/CriteriaSheetGeneratorTests.cs index 39b4d03..91080e2 100644 --- a/NEW/tests/JdeScoping.ExcelIO.Tests/CriteriaSheetGeneratorTests.cs +++ b/NEW/tests/JdeScoping.ExcelIO.Tests/CriteriaSheetGeneratorTests.cs @@ -1,7 +1,8 @@ using ClosedXML.Excel; using JdeScoping.ExcelIO.Options; using JdeScoping.ExcelIO.Generators; -using JdeScoping.ExcelIO.Helpers; +using JdeScoping.ExcelIO.Mapping; +using JdeScoping.ExcelIO.Mapping.Maps; using JdeScoping.ExcelIO.Models.Reporting; using Microsoft.Extensions.Options; using Shouldly; @@ -21,11 +22,25 @@ public class CriteriaSheetGeneratorTests CriteriaSheetPassword = "TestPassword" }); - var cache = new OutputColumnCache(); - var tableWriter = new AttributeTableWriter(cache); + var registry = CreateTestRegistry(); + var tableWriter = new FluentTableWriter(registry); _generator = new CriteriaSheetGenerator(_options, tableWriter); } + private static ExcelMapRegistry CreateTestRegistry() + { + var registry = new ExcelMapRegistry(); + registry.Register(new TimespanFilterMap()); + registry.Register(new WorkOrderFilterEntryMap()); + registry.Register(new ItemNumberFilterEntryMap()); + registry.Register(new ProfitCenterFilterEntryMap()); + registry.Register(new WorkCenterFilterEntryMap()); + registry.Register(new OperatorFilterEntryMap()); + registry.Register(new ComponentLotFilterEntryMap()); + registry.Register(new ItemOperationMisFilterEntryMap()); + return registry; + } + [Fact] public void Generate_CreatesSearchCriteriaSheet() { diff --git a/NEW/tests/JdeScoping.ExcelIO.Tests/ExcelExportIntegrationTests.cs b/NEW/tests/JdeScoping.ExcelIO.Tests/ExcelExportIntegrationTests.cs index f4e148b..6f4d9e9 100644 --- a/NEW/tests/JdeScoping.ExcelIO.Tests/ExcelExportIntegrationTests.cs +++ b/NEW/tests/JdeScoping.ExcelIO.Tests/ExcelExportIntegrationTests.cs @@ -1,7 +1,8 @@ using ClosedXML.Excel; using JdeScoping.ExcelIO.Options; using JdeScoping.ExcelIO.Generators; -using JdeScoping.ExcelIO.Helpers; +using JdeScoping.ExcelIO.Mapping; +using JdeScoping.ExcelIO.Mapping.Maps; using JdeScoping.ExcelIO.Models.Reporting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -9,6 +10,10 @@ using NSubstitute; using Shouldly; using Xunit; +using SearchResult = JdeScoping.Core.Models.SearchResults.SearchResult; +using MisSearchResult = JdeScoping.Core.Models.SearchResults.MisSearchResult; +using MisNonMatchSearchResult = JdeScoping.Core.Models.SearchResults.MisNonMatchSearchResult; + namespace JdeScoping.ExcelIO.Tests; /// @@ -29,11 +34,33 @@ public class ExcelExportIntegrationTests DataSheetPassword = "TestDataPass" }); - var cache = new OutputColumnCache(); - var tableWriter = new AttributeTableWriter(cache); + var registry = CreateTestRegistry(); + var tableWriter = new FluentTableWriter(registry); var criteriaGenerator = new CriteriaSheetGenerator(_options, tableWriter); - _service = new ExcelExportService(_logger, _options, criteriaGenerator, tableWriter); + _service = new ExcelExportService(_logger, _options, criteriaGenerator, tableWriter, registry); + } + + private static ExcelMapRegistry CreateTestRegistry() + { + var registry = new ExcelMapRegistry(); + + // Search result maps + registry.Register(new SearchResultMap()); + registry.Register(new MisSearchResultMap()); + registry.Register(new MisNonMatchSearchResultMap()); + + // Filter entry maps + registry.Register(new TimespanFilterMap()); + registry.Register(new WorkOrderFilterEntryMap()); + registry.Register(new ItemNumberFilterEntryMap()); + registry.Register(new ProfitCenterFilterEntryMap()); + registry.Register(new WorkCenterFilterEntryMap()); + registry.Register(new OperatorFilterEntryMap()); + registry.Register(new ComponentLotFilterEntryMap()); + registry.Register(new ItemOperationMisFilterEntryMap()); + + return registry; } #region Sheet Count Tests @@ -387,10 +414,13 @@ public class ExcelExportIntegrationTests public async Task GenerateAsync_SearchResults_ContainsCorrectData() { var search = CreateMinimalSearchModel(); - var searchResult = CreateSampleSearchResult(); - searchResult.WorkOrderNumber = 99999; - searchResult.ItemNumber = "TEST-ITEM-001"; - searchResult.LotNumber = "LOT-999"; + var searchResult = new SearchResult + { + WorkOrderNumber = 99999, + ItemNumber = "TEST-ITEM-001", + LotNumber = "LOT-999", + Flagged = true + }; search.Results.Add(searchResult); var result = await _service.GenerateAsync(search); @@ -408,9 +438,16 @@ public class ExcelExportIntegrationTests [Fact] public async Task GenerateAsync_MisInfo_ContainsCorrectData() { - var search = CreateSearchModelWithMisData(); - search.MisResults![0].ItemNumber = "MIS-ITEM-001"; - search.MisResults[0].MisNumber = "MIS-12345"; + var search = CreateMinimalSearchModel(); + search.ExtractMisData = true; + search.MisResults = [ + new MisSearchResult + { + ItemNumber = "MIS-ITEM-001", + MisNumber = "MIS-12345" + } + ]; + search.MisNonMatchResults = []; var result = await _service.GenerateAsync(search); @@ -426,9 +463,16 @@ public class ExcelExportIntegrationTests [Fact] public async Task GenerateAsync_Investigation_ContainsCorrectData() { - var search = CreateSearchModelWithMisData(); - search.MisNonMatchResults![0].WorkOrderNumber = 77777; - search.MisNonMatchResults[0].ItemNumber = "INV-ITEM-001"; + var search = CreateMinimalSearchModel(); + search.ExtractMisData = true; + search.MisResults = []; + search.MisNonMatchResults = [ + new MisNonMatchSearchResult + { + WorkOrderNumber = 77777, + ItemNumber = "INV-ITEM-001" + } + ]; var result = await _service.GenerateAsync(search); diff --git a/NEW/tests/JdeScoping.ExcelIO.Tests/ExcelExportServiceTests.cs b/NEW/tests/JdeScoping.ExcelIO.Tests/ExcelExportServiceTests.cs index 15fc283..1e4e5bb 100644 --- a/NEW/tests/JdeScoping.ExcelIO.Tests/ExcelExportServiceTests.cs +++ b/NEW/tests/JdeScoping.ExcelIO.Tests/ExcelExportServiceTests.cs @@ -1,7 +1,8 @@ using ClosedXML.Excel; using JdeScoping.ExcelIO.Options; using JdeScoping.ExcelIO.Generators; -using JdeScoping.ExcelIO.Helpers; +using JdeScoping.ExcelIO.Mapping; +using JdeScoping.ExcelIO.Mapping.Maps; using JdeScoping.ExcelIO.Models.Reporting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -9,6 +10,10 @@ using NSubstitute; using Shouldly; using Xunit; +using SearchResult = JdeScoping.Core.Models.SearchResults.SearchResult; +using MisSearchResult = JdeScoping.Core.Models.SearchResults.MisSearchResult; +using MisNonMatchSearchResult = JdeScoping.Core.Models.SearchResults.MisNonMatchSearchResult; + namespace JdeScoping.ExcelIO.Tests; public class ExcelExportServiceTests @@ -26,11 +31,33 @@ public class ExcelExportServiceTests DataSheetPassword = "TestDataPass" }); - var cache = new OutputColumnCache(); - var tableWriter = new AttributeTableWriter(cache); + var registry = CreateTestRegistry(); + var tableWriter = new FluentTableWriter(registry); var criteriaGenerator = new CriteriaSheetGenerator(_options, tableWriter); - _service = new ExcelExportService(_logger, _options, criteriaGenerator, tableWriter); + _service = new ExcelExportService(_logger, _options, criteriaGenerator, tableWriter, registry); + } + + private static ExcelMapRegistry CreateTestRegistry() + { + var registry = new ExcelMapRegistry(); + + // Search result maps + registry.Register(new SearchResultMap()); + registry.Register(new MisSearchResultMap()); + registry.Register(new MisNonMatchSearchResultMap()); + + // Filter entry maps + registry.Register(new TimespanFilterMap()); + registry.Register(new WorkOrderFilterEntryMap()); + registry.Register(new ItemNumberFilterEntryMap()); + registry.Register(new ProfitCenterFilterEntryMap()); + registry.Register(new WorkCenterFilterEntryMap()); + registry.Register(new OperatorFilterEntryMap()); + registry.Register(new ComponentLotFilterEntryMap()); + registry.Register(new ItemOperationMisFilterEntryMap()); + + return registry; } [Fact] diff --git a/NEW/tests/JdeScoping.ExcelIO.Tests/InclusionReasonTests.cs b/NEW/tests/JdeScoping.ExcelIO.Tests/InclusionReasonTests.cs index 5e39e93..c35007e 100644 --- a/NEW/tests/JdeScoping.ExcelIO.Tests/InclusionReasonTests.cs +++ b/NEW/tests/JdeScoping.ExcelIO.Tests/InclusionReasonTests.cs @@ -1,4 +1,4 @@ -using JdeScoping.ExcelIO.Models.Reporting; +using JdeScoping.Core.Models.SearchResults; using Shouldly; using Xunit; diff --git a/NEW/tests/JdeScoping.ExcelIO.Tests/LegacyComparisonTests.cs b/NEW/tests/JdeScoping.ExcelIO.Tests/LegacyComparisonTests.cs index 368abb0..17b9619 100644 --- a/NEW/tests/JdeScoping.ExcelIO.Tests/LegacyComparisonTests.cs +++ b/NEW/tests/JdeScoping.ExcelIO.Tests/LegacyComparisonTests.cs @@ -1,8 +1,8 @@ using ClosedXML.Excel; using JdeScoping.ExcelIO.Options; -using JdeScoping.ExcelIO.Formatting; using JdeScoping.ExcelIO.Generators; -using JdeScoping.ExcelIO.Helpers; +using JdeScoping.ExcelIO.Mapping; +using JdeScoping.ExcelIO.Mapping.Maps; using JdeScoping.ExcelIO.Models.Reporting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -10,6 +10,11 @@ using NSubstitute; using Shouldly; using Xunit; +using ExcelFormats = JdeScoping.ExcelIO.Formatting.ExcelFormats; +using SearchResult = JdeScoping.Core.Models.SearchResults.SearchResult; +using MisSearchResult = JdeScoping.Core.Models.SearchResults.MisSearchResult; +using MisNonMatchSearchResult = JdeScoping.Core.Models.SearchResults.MisNonMatchSearchResult; + namespace JdeScoping.ExcelIO.Tests; /// @@ -30,11 +35,33 @@ public class LegacyComparisonTests DataSheetPassword = "JDESCOPINGTOOL" }); - var cache = new OutputColumnCache(); - var tableWriter = new AttributeTableWriter(cache); + var registry = CreateTestRegistry(); + var tableWriter = new FluentTableWriter(registry); var criteriaGenerator = new CriteriaSheetGenerator(options, tableWriter); - _service = new ExcelExportService(logger, options, criteriaGenerator, tableWriter); + _service = new ExcelExportService(logger, options, criteriaGenerator, tableWriter, registry); + } + + private static ExcelMapRegistry CreateTestRegistry() + { + var registry = new ExcelMapRegistry(); + + // Search result maps + registry.Register(new SearchResultMap()); + registry.Register(new MisSearchResultMap()); + registry.Register(new MisNonMatchSearchResultMap()); + + // Filter entry maps + registry.Register(new TimespanFilterMap()); + registry.Register(new WorkOrderFilterEntryMap()); + registry.Register(new ItemNumberFilterEntryMap()); + registry.Register(new ProfitCenterFilterEntryMap()); + registry.Register(new WorkCenterFilterEntryMap()); + registry.Register(new OperatorFilterEntryMap()); + registry.Register(new ComponentLotFilterEntryMap()); + registry.Register(new ItemOperationMisFilterEntryMap()); + + return registry; } #region Search Results Column Order Tests diff --git a/NEW/tests/JdeScoping.ExcelIO.Tests/Mapping/ExcelClassMapTests.cs b/NEW/tests/JdeScoping.ExcelIO.Tests/Mapping/ExcelClassMapTests.cs index 2ef42c9..c37285b 100644 --- a/NEW/tests/JdeScoping.ExcelIO.Tests/Mapping/ExcelClassMapTests.cs +++ b/NEW/tests/JdeScoping.ExcelIO.Tests/Mapping/ExcelClassMapTests.cs @@ -1,3 +1,4 @@ +using JdeScoping.ExcelIO.Formatting; using JdeScoping.ExcelIO.Mapping; using Shouldly; using Xunit; @@ -23,7 +24,7 @@ public class ExcelClassMapTests Map(x => x.Id).Order(10).Header("ID Number"); Map(x => x.Name).Order(20).Header("Full Name"); - Map(x => x.CreatedAt).Order(30).Header("Created").Format(ExcelFormats.Timestamp); + Map(x => x.CreatedAt).Order(30).Header("Created").Format(ExcelFormats.TimestampFormat); } } @@ -71,7 +72,7 @@ public class ExcelClassMapTests var map = new TestModelMap(); var columns = map.Columns; - columns[2].Format.ShouldBe(ExcelFormats.Timestamp); + columns[2].Format.ShouldBe(ExcelFormats.TimestampFormat); } [Fact] diff --git a/NEW/tests/JdeScoping.ExcelIO.Tests/OutputColumnCacheTests.cs b/NEW/tests/JdeScoping.ExcelIO.Tests/OutputColumnCacheTests.cs deleted file mode 100644 index 8685d57..0000000 --- a/NEW/tests/JdeScoping.ExcelIO.Tests/OutputColumnCacheTests.cs +++ /dev/null @@ -1,100 +0,0 @@ -using JdeScoping.ExcelIO.Attributes; -using JdeScoping.ExcelIO.Helpers; -using Shouldly; -using Xunit; - -namespace JdeScoping.ExcelIO.Tests; - -public class OutputColumnCacheTests -{ - private readonly OutputColumnCache _cache = new(); - - [OutputTable(TabName = "Test Table", TableName = "Test_Table")] - private class TestModel - { - [OutputColumn(Order = 30, HeaderText = "Column C")] - public string ColumnC { get; set; } = string.Empty; - - [OutputColumn(Order = 10, HeaderText = "Column A")] - public string ColumnA { get; set; } = string.Empty; - - [OutputColumn(Order = 20, HeaderText = "Column B")] - public string ColumnB { get; set; } = string.Empty; - - public string NonOutputColumn { get; set; } = string.Empty; - } - - private class TieBreakModel - { - [OutputColumn(Order = 10, HeaderText = "Zebra")] - public string Zebra { get; set; } = string.Empty; - - [OutputColumn(Order = 10, HeaderText = "Apple")] - public string Apple { get; set; } = string.Empty; - - [OutputColumn(Order = 10, HeaderText = "Mango")] - public string Mango { get; set; } = string.Empty; - } - - private class EmptyModel - { - public string NoAttributes { get; set; } = string.Empty; - } - - [Fact] - public void GetColumns_ReturnsColumnsOrderedByOrderProperty() - { - var columns = _cache.GetColumns(); - - columns.Count.ShouldBe(3); - columns[0].Attribute.HeaderText.ShouldBe("Column A"); - columns[1].Attribute.HeaderText.ShouldBe("Column B"); - columns[2].Attribute.HeaderText.ShouldBe("Column C"); - } - - [Fact] - public void GetColumns_TieBreaksAlphabeticallyByPropertyName() - { - var columns = _cache.GetColumns(); - - columns.Count.ShouldBe(3); - // All have Order=10, so should be sorted by property name - columns[0].Name.ShouldBe("Apple"); - columns[1].Name.ShouldBe("Mango"); - columns[2].Name.ShouldBe("Zebra"); - } - - [Fact] - public void GetColumns_ExcludesPropertiesWithoutAttribute() - { - var columns = _cache.GetColumns(); - - columns.Count.ShouldBe(3); - columns.ShouldNotContain(c => c.Name == "NonOutputColumn"); - } - - [Fact] - public void GetColumns_ReturnsEmptyForEmptyModel() - { - var columns = _cache.GetColumns(); - - columns.Count.ShouldBe(0); - } - - [Fact] - public void GetColumns_CachesResults() - { - var columns1 = _cache.GetColumns(); - var columns2 = _cache.GetColumns(); - - ReferenceEquals(columns1, columns2).ShouldBeTrue(); - } - - [Fact] - public void GetColumns_ByType_ReturnsCorrectColumns() - { - var columns = _cache.GetColumns(typeof(TestModel)); - - columns.Count.ShouldBe(3); - } -}