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.
This commit is contained in:
Joseph Doherty
2026-01-06 23:56:02 -05:00
parent e98ce636e2
commit 621dd41a97
30 changed files with 287 additions and 891 deletions
@@ -1,58 +0,0 @@
namespace JdeScoping.ExcelIO.Attributes;
/// <summary>
/// Excel output column specification.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class OutputColumnAttribute : Attribute
{
/// <summary>
/// Standard format (text).
/// </summary>
public const string StdFormat = "@";
/// <summary>
/// Standard date format.
/// </summary>
public const string DateFormat = "[$-409]MM/dd/yyyy;@";
/// <summary>
/// Standard timestamp format.
/// </summary>
public const string TimestampFormat = "[$-409]m/d/yy h:mm AM/PM;@";
/// <summary>
/// Wrapped text column default width.
/// </summary>
public const double WrappedColumnWidth = 65;
/// <summary>
/// Order to display column.
/// </summary>
public int Order { get; set; }
/// <summary>
/// Override text to display for column header.
/// </summary>
public string HeaderText { get; set; } = string.Empty;
/// <summary>
/// Column format (Excel formatting string).
/// </summary>
public string Format { get; set; } = StdFormat;
/// <summary>
/// Whether or not width should be set automatically.
/// </summary>
public bool AutoWidth { get; set; } = true;
/// <summary>
/// Manually set width (only used if AutoWidth = false).
/// </summary>
public double Width { get; set; }
/// <summary>
/// Whether or not text should be wrapped.
/// </summary>
public bool WrapText { get; set; }
}
@@ -1,23 +0,0 @@
namespace JdeScoping.ExcelIO.Attributes;
/// <summary>
/// Excel output table specification.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class OutputTableAttribute : Attribute
{
/// <summary>
/// Output tab name.
/// </summary>
public string TabName { get; set; } = string.Empty;
/// <summary>
/// Table name.
/// </summary>
public string TableName { get; set; } = string.Empty;
/// <summary>
/// Whether or not merged header should be shown.
/// </summary>
public bool ShowHeader { get; set; }
}
@@ -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<CriteriaSheetGenerator>();
// Register helpers (singleton - stateless)
services.AddSingleton<OutputColumnCache>();
services.AddSingleton<AttributeTableWriter>();
// Register template generator (singleton - stateless)
services.AddSingleton<DataEntryTemplateGenerator>();
// 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;
});
@@ -1,54 +0,0 @@
using ClosedXML.Excel;
using JdeScoping.ExcelIO.Attributes;
namespace JdeScoping.ExcelIO.Formatting;
/// <summary>
/// Column width and number format utilities.
/// </summary>
public static class ColumnFormatter
{
/// <summary>
/// Applies column formatting based on attribute settings.
/// </summary>
/// <param name="column">The column to format.</param>
/// <param name="attr">The output column attribute with settings.</param>
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;
}
}
/// <summary>
/// Auto-fits a column with the specified padding factor.
/// </summary>
/// <param name="column">The column to auto-fit.</param>
/// <param name="paddingFactor">The padding factor to apply (e.g., 1.15 for 15% padding).</param>
public static void AutoFitWithPadding(IXLColumn column, double paddingFactor)
{
column.AdjustToContents();
column.Width *= paddingFactor;
}
}
@@ -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;
/// <summary>
/// Generic attribute-driven table writer for Excel worksheets.
/// </summary>
public class AttributeTableWriter
{
private readonly OutputColumnCache _cache;
/// <summary>
/// Initializes a new instance of the AttributeTableWriter class.
/// </summary>
/// <param name="cache">The output column cache.</param>
public AttributeTableWriter(OutputColumnCache cache)
{
_cache = cache;
}
/// <summary>
/// Writes a table to the worksheet using attribute-driven column definitions.
/// </summary>
/// <typeparam name="T">The type of data items.</typeparam>
/// <param name="worksheet">The worksheet to write to.</param>
/// <param name="startRow">The starting row (1-indexed).</param>
/// <param name="startCol">The starting column (1-indexed).</param>
/// <param name="data">The data to write.</param>
/// <param name="tableNameOverride">Optional table name override.</param>
/// <param name="showHeader">Optional override for showing merged header.</param>
/// <param name="headerText">Optional header text for merged header.</param>
/// <returns>The created table, or null if no data.</returns>
public IXLTable? WriteTable<T>(
IXLWorksheet worksheet,
int startRow,
int startCol,
IEnumerable<T> data,
string? tableNameOverride = null,
bool? showHeader = null,
string? headerText = null)
{
var tableAttr = typeof(T).GetCustomAttribute<OutputTableAttribute>();
var columns = _cache.GetColumns<T>();
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;
}
/// <summary>
/// Converts a value to an XLCellValue.
/// </summary>
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
};
}
}
@@ -12,16 +12,16 @@ namespace JdeScoping.ExcelIO.Generators;
public class CriteriaSheetGenerator
{
private readonly IOptions<ExcelExportOptions> _options;
private readonly AttributeTableWriter _tableWriter;
private readonly FluentTableWriter _tableWriter;
/// <summary>
/// Initializes a new instance of the CriteriaSheetGenerator class.
/// </summary>
/// <param name="options">Excel export options.</param>
/// <param name="tableWriter">Attribute table writer.</param>
/// <param name="tableWriter">Fluent table writer.</param>
public CriteriaSheetGenerator(
IOptions<ExcelExportOptions> options,
AttributeTableWriter tableWriter)
FluentTableWriter tableWriter)
{
_options = options;
_tableWriter = tableWriter;
@@ -1,60 +0,0 @@
using System.Collections.Concurrent;
using System.Reflection;
using JdeScoping.ExcelIO.Attributes;
using JdeScoping.ExcelIO.Models;
namespace JdeScoping.ExcelIO.Helpers;
/// <summary>
/// Cached reflection for column metadata.
/// </summary>
public class OutputColumnCache
{
private readonly ConcurrentDictionary<Type, IReadOnlyList<OutputColumn>> _cache = new();
/// <summary>
/// Gets the output columns for a given type.
/// </summary>
/// <typeparam name="T">The type to get columns for.</typeparam>
/// <returns>Ordered list of output columns.</returns>
public IReadOnlyList<OutputColumn> GetColumns<T>()
{
return GetColumns(typeof(T));
}
/// <summary>
/// Gets the output columns for a given type.
/// </summary>
/// <param name="type">The type to get columns for.</param>
/// <returns>Ordered list of output columns.</returns>
public IReadOnlyList<OutputColumn> GetColumns(Type type)
{
return _cache.GetOrAdd(type, BuildColumns);
}
private static IReadOnlyList<OutputColumn> BuildColumns(Type type)
{
var columns = new List<OutputColumn>();
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
var attr = property.GetCustomAttribute<OutputColumnAttribute>();
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();
}
}
@@ -1,19 +0,0 @@
namespace JdeScoping.ExcelIO.Mapping;
/// <summary>
/// Standard Excel format strings for column configuration.
/// </summary>
public static class ExcelFormats
{
/// <summary>Text format (default).</summary>
public const string Text = "@";
/// <summary>Date format: MM/dd/yyyy</summary>
public const string Date = "[$-409]MM/dd/yyyy;@";
/// <summary>Timestamp format: m/d/yy h:mm AM/PM</summary>
public const string Timestamp = "[$-409]m/d/yy h:mm AM/PM;@";
/// <summary>Default width for wrapped text columns.</summary>
public const double WrappedColumnWidth = 65;
}
@@ -0,0 +1,119 @@
using JdeScoping.ExcelIO.Formatting;
using JdeScoping.ExcelIO.Models.Reporting;
namespace JdeScoping.ExcelIO.Mapping.Maps;
/// <summary>
/// Excel column mapping for TimespanFilter.
/// </summary>
public sealed class TimespanFilterMap : ExcelClassMap<TimespanFilter>
{
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);
}
}
/// <summary>
/// Excel column mapping for WorkOrderFilterEntry.
/// </summary>
public sealed class WorkOrderFilterEntryMap : ExcelClassMap<WorkOrderFilterEntry>
{
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");
}
}
/// <summary>
/// Excel column mapping for ItemNumberFilterEntry.
/// </summary>
public sealed class ItemNumberFilterEntryMap : ExcelClassMap<ItemNumberFilterEntry>
{
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");
}
}
/// <summary>
/// Excel column mapping for ProfitCenterFilterEntry.
/// </summary>
public sealed class ProfitCenterFilterEntryMap : ExcelClassMap<ProfitCenterFilterEntry>
{
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");
}
}
/// <summary>
/// Excel column mapping for WorkCenterFilterEntry.
/// </summary>
public sealed class WorkCenterFilterEntryMap : ExcelClassMap<WorkCenterFilterEntry>
{
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");
}
}
/// <summary>
/// Excel column mapping for OperatorFilterEntry.
/// </summary>
public sealed class OperatorFilterEntryMap : ExcelClassMap<OperatorFilterEntry>
{
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");
}
}
/// <summary>
/// Excel column mapping for ComponentLotFilterEntry.
/// </summary>
public sealed class ComponentLotFilterEntryMap : ExcelClassMap<ComponentLotFilterEntry>
{
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");
}
}
/// <summary>
/// Excel column mapping for ItemOperationMisFilterEntry.
/// </summary>
public sealed class ItemOperationMisFilterEntryMap : ExcelClassMap<ItemOperationMisFilterEntry>
{
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");
}
}
@@ -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<MisNonMatchSearch
Map(x => 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");
@@ -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<MisSearchResult>
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");
@@ -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<SearchResult>
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");
}
}
@@ -1,25 +0,0 @@
using System.Reflection;
using JdeScoping.ExcelIO.Attributes;
namespace JdeScoping.ExcelIO.Models;
/// <summary>
/// Column metadata model for Excel output.
/// </summary>
public class OutputColumn
{
/// <summary>
/// Property name.
/// </summary>
public string Name { get; init; } = string.Empty;
/// <summary>
/// Property info for reflection.
/// </summary>
public PropertyInfo Property { get; init; } = null!;
/// <summary>
/// Output column attribute.
/// </summary>
public OutputColumnAttribute Attribute { get; init; } = null!;
}
@@ -1,22 +1,17 @@
using JdeScoping.ExcelIO.Attributes;
namespace JdeScoping.ExcelIO.Models.Reporting;
/// <summary>
/// Component lot search filter entry.
/// </summary>
[OutputTable(TabName = "Component Lot Filter", ShowHeader = true, TableName = "Component_Lot_Filter")]
public class ComponentLotFilterEntry
{
/// <summary>
/// Component lot number.
/// </summary>
[OutputColumn(Order = 10, HeaderText = "Lot Number")]
public string LotNumber { get; set; } = string.Empty;
/// <summary>
/// Component lot item number.
/// </summary>
[OutputColumn(Order = 20, HeaderText = "Item Number")]
public string ItemNumber { get; set; } = string.Empty;
}
@@ -1,22 +1,17 @@
using JdeScoping.ExcelIO.Attributes;
namespace JdeScoping.ExcelIO.Models.Reporting;
/// <summary>
/// Item number search filter entry.
/// </summary>
[OutputTable(TabName = "Item Number Filter", ShowHeader = true, TableName = "Item_Number_Filter")]
public class ItemNumberFilterEntry
{
/// <summary>
/// Item number.
/// </summary>
[OutputColumn(Order = 10, HeaderText = "Item Number")]
public string ItemNumber { get; set; } = string.Empty;
/// <summary>
/// Item description.
/// </summary>
[OutputColumn(Order = 20, HeaderText = "Item Description")]
public string ItemDescription { get; set; } = string.Empty;
}
@@ -1,34 +1,27 @@
using JdeScoping.ExcelIO.Attributes;
namespace JdeScoping.ExcelIO.Models.Reporting;
/// <summary>
/// Item/Operation/MIS search filter entry.
/// </summary>
[OutputTable(TabName = "Item/Operation/MIS Filter", ShowHeader = true, TableName = "Item_Operation_MIS_Filter")]
public class ItemOperationMisFilterEntry
{
/// <summary>
/// Part's item number.
/// </summary>
[OutputColumn(Order = 10, HeaderText = "Item Number")]
public string ItemNumber { get; set; } = string.Empty;
/// <summary>
/// Operation's job step number.
/// </summary>
[OutputColumn(Order = 20, HeaderText = "Operation Number")]
public string OperationNumber { get; set; } = string.Empty;
/// <summary>
/// MIS number.
/// </summary>
[OutputColumn(Order = 30, HeaderText = "MIS Number")]
public string MisNumber { get; set; } = string.Empty;
/// <summary>
/// MIS revision.
/// </summary>
[OutputColumn(Order = 40, HeaderText = "MIS Revision")]
public string MisRevision { get; set; } = string.Empty;
}
@@ -1,11 +1,8 @@
using JdeScoping.ExcelIO.Attributes;
namespace JdeScoping.ExcelIO.Models.Reporting;
/// <summary>
/// Operator search filter entry.
/// </summary>
[OutputTable(TabName = "Operator Filter", ShowHeader = true, TableName = "Operator_Filter")]
public class OperatorFilterEntry
{
/// <summary>
@@ -16,12 +13,10 @@ public class OperatorFilterEntry
/// <summary>
/// Operator login user ID.
/// </summary>
[OutputColumn(Order = 10, HeaderText = "Username")]
public string UserId { get; set; } = string.Empty;
/// <summary>
/// Operator full name (FIRST + LAST).
/// </summary>
[OutputColumn(Order = 20, HeaderText = "Name")]
public string FullName { get; set; } = string.Empty;
}
@@ -1,22 +1,17 @@
using JdeScoping.ExcelIO.Attributes;
namespace JdeScoping.ExcelIO.Models.Reporting;
/// <summary>
/// Profit center search filter entry.
/// </summary>
[OutputTable(TabName = "Profit Center Filter", ShowHeader = true, TableName = "Profit_Center_Filter")]
public class ProfitCenterFilterEntry
{
/// <summary>
/// Profit center code.
/// </summary>
[OutputColumn(Order = 10, HeaderText = "Profit Center")]
public string Code { get; set; } = string.Empty;
/// <summary>
/// Profit center description.
/// </summary>
[OutputColumn(Order = 20, HeaderText = "Description")]
public string Description { get; set; } = string.Empty;
}
@@ -1,22 +1,17 @@
using JdeScoping.ExcelIO.Attributes;
namespace JdeScoping.ExcelIO.Models.Reporting;
/// <summary>
/// Timespan filter entry for criteria sheet.
/// </summary>
[OutputTable(TabName = "Timespan Filter", ShowHeader = true, TableName = "Timespan_Filter")]
public class TimespanFilter
{
/// <summary>
/// Minimum date/time.
/// </summary>
[OutputColumn(Order = 10, HeaderText = "Minimum Date", Format = OutputColumnAttribute.DateFormat)]
public DateTime? MinimumDt { get; set; }
/// <summary>
/// Maximum date/time.
/// </summary>
[OutputColumn(Order = 20, HeaderText = "Maximum Date", Format = OutputColumnAttribute.DateFormat)]
public DateTime? MaximumDt { get; set; }
}
@@ -1,22 +1,17 @@
using JdeScoping.ExcelIO.Attributes;
namespace JdeScoping.ExcelIO.Models.Reporting;
/// <summary>
/// Work center search filter entry.
/// </summary>
[OutputTable(TabName = "Work Center Filter", ShowHeader = true, TableName = "Work_Center_Filter")]
public class WorkCenterFilterEntry
{
/// <summary>
/// Work center code.
/// </summary>
[OutputColumn(Order = 10, HeaderText = "Work Center")]
public string Code { get; set; } = string.Empty;
/// <summary>
/// Work center description.
/// </summary>
[OutputColumn(Order = 20, HeaderText = "Description")]
public string Description { get; set; } = string.Empty;
}
@@ -1,22 +1,17 @@
using JdeScoping.ExcelIO.Attributes;
namespace JdeScoping.ExcelIO.Models.Reporting;
/// <summary>
/// Work order search filter entry.
/// </summary>
[OutputTable(TabName = "Work Order Filter", ShowHeader = true, TableName = "Work_Order_Filter")]
public class WorkOrderFilterEntry
{
/// <summary>
/// Work order number.
/// </summary>
[OutputColumn(Order = 10, HeaderText = "Work Order Number")]
public long WorkOrderNumber { get; set; }
/// <summary>
/// Work order item number.
/// </summary>
[OutputColumn(Order = 20, HeaderText = "Item Number")]
public string ItemNumber { get; set; } = string.Empty;
}