Set up repository with legacy .NET Framework 4.8 source (OLD/), new .NET 10 Blazor solution (NEW/), OpenSpec specifications, documentation, and project configuration.
29 KiB
Excel Export Specification
Purpose
The Excel Export subsystem generates multi-sheet Excel workbooks (.xlsx) containing search results and criteria documentation using the ClosedXML library on .NET 10. It provides an injectable IExcelExportService that transforms search data into formatted, protected spreadsheets that users can download, analyze, and share. The subsystem supports conditional sheet generation based on search options (MIS data extraction) and applies consistent styling, column definitions, and worksheet protection across all output.
Source Reference
| Legacy File | Purpose |
|---|---|
| OLD/WorkerService/Process/ExcelWriter.cs | Main Excel generation orchestration and sheet writing |
| OLD/WorkerService/Helpers/ExcelHelpers.cs | Generic table loading and attribute-driven column formatting |
| OLD/WebInterface/Helpers/ExcelTemplateGenerator.cs | Data entry template generation for bulk uploads |
| OLD/WorkerService/Models/Reporting/OutputColumnAttribute.cs | Column metadata (order, format, width, wrap) |
| OLD/WorkerService/Models/Reporting/OutputTableAttribute.cs | Table/tab metadata (name, header display) |
| OLD/WorkerService/Models/Reporting/SearchResult.cs | Search results data model with column definitions |
| OLD/WorkerService/Models/Reporting/MisSearchResult.cs | MIS data model with column definitions |
| OLD/WorkerService/Models/Reporting/MisNonMatchSearchResult.cs | Investigation (mismatch) data model |
| OLD/WorkerService/Models/Reporting/*.cs | Filter entry models for criteria documentation |
Requirements
Requirement: Injectable Excel Export Service
The system SHALL provide an injectable service for Excel generation following .NET dependency injection patterns.
Service Interface
public interface IExcelExportService
{
Task<byte[]> GenerateAsync(SearchModel search, CancellationToken cancellationToken = default);
Task GenerateToStreamAsync(SearchModel search, Stream outputStream, CancellationToken cancellationToken = default);
}
Test Support
For unit testing, the system SHALL provide mock data factories to generate sample data without database dependencies:
public static class ExcelTestDataFactory
{
public static SearchModel CreateSampleSearch(int resultCount = 10);
public static List<SearchResult> CreateSampleResults(int count);
public static List<MisSearchResult> CreateSampleMisResults(int count);
public static List<MisNonMatchSearchResult> CreateSampleInvestigationResults(int count);
}
Configuration Class
public class ExcelExportOptions
{
public string CriteriaSheetPassword { get; set; } = "JDE_SCOPING_TOOL_PASS";
public string DataSheetPassword { get; set; } = "JDESCOPINGTOOL";
}
Business Rules
- The service MUST be registered as scoped or transient in the DI container
- The service MUST accept
ILogger<ExcelExportService>for structured logging - The service MUST accept
IOptions<ExcelExportOptions>for configuration - Logging MUST use
BeginScope()to include search context (SearchId, SearchName) - The service MUST use ClosedXML (
XLWorkbook) for workbook generation - Temporary files SHALL use
Path.GetTempPath()for cross-platform temp directory access - Debug file output (
DebugWriteToFile) SHALL be disabled by default; enable via configuration for troubleshooting
Scenario: Register service in DI container
- WHEN the application starts
- THEN
IExcelExportServiceis registered withExcelExportServiceimplementation as scoped
Scenario: Log export operations with context
- WHEN generating an export for a search
- THEN log entries include SearchId and SearchName via
ILogger.BeginScope()
Requirement: Multi-Sheet Workbook Generation
The system SHALL generate Excel workbooks with multiple worksheets based on search configuration and results.
Inputs
SearchModelcontaining:- Search metadata (name, username, timestamps)
- Filter criteria (timespan, work orders, items, profit centers, work centers, operators, component lots, item/operation/MIS)
- Search results (
List<SearchResult>) - MIS results (
List<MisSearchResult>) - when ExtractMisData is enabled - MIS non-match results (
List<MisNonMatchSearchResult>) - when ExtractMisData is enabled
ExtractMisDataboolean flag
Outputs
- Excel workbook as
byte[]containing:- "Search Criteria" sheet (always present)
- "Search Results" sheet (always present)
- "MIS Info" sheet (conditional - only when ExtractMisData is true and results not null)
- "Investigation" sheet (conditional - only when ExtractMisData is true and results not null)
Business Rules
- The "Search Criteria" sheet MUST always be the first sheet in the workbook
- The "Search Results" sheet MUST always be the second sheet
- MIS-related sheets SHALL only be included when
ExtractMisDatais true AND respective result collections are not null - The workbook MUST be returned as a byte array for storage in the database
Scenario: Generate standard search export
- WHEN a search completes with ExtractMisData set to false
- THEN the system creates a workbook with exactly two sheets: "Search Criteria" and "Search Results"
Scenario: Generate full export with MIS data
- WHEN a search completes with ExtractMisData set to true and both MIS collections populated
- THEN the system creates a workbook with four sheets: "Search Criteria", "Search Results", "MIS Info", and "Investigation"
Scenario: Generate export with empty MIS results
- WHEN a search completes with ExtractMisData true but MisResults is empty (not null)
- THEN the system creates the "MIS Info" sheet with empty data table
Scenario: Generate export with null MIS results
- WHEN a search completes with ExtractMisData true but MisResults is null
- THEN the system skips the "MIS Info" sheet entirely
Scenario: Generate export with null investigation results
- WHEN a search completes with ExtractMisData true but MisNonMatchResults is null
- THEN the system skips the "Investigation" sheet entirely
Requirement: Search Criteria Documentation Sheet
The system SHALL generate a "Search Criteria" sheet documenting all search parameters and execution metadata.
Inputs
- Search metadata: Name, UserName, SubmitDT, StartDT, EndDT
- Timespan filter: MinimumDT, MaximumDT
- Filter collections: WorkOrderFilter, ItemNumberFilter, ProfitCenterFilter, WorkCenterFilter, OperatorFilter, ComponentLotFilter, ItemOperationMisFilter
- ExtractMisData flag
Outputs
- Worksheet named "Search Criteria" containing:
- Search name and username header rows
- Timestamp section (submit, start, completed)
- Timespan filter table
- Multiple filter tables (one per filter type)
- Extract MIS data indicator
Business Rules
- Header cells MUST use bold, centered text with Gainsboro (light gray) background via
XLColor.Gainsboro - Timestamps MUST be formatted as "MMM dd, yyyy hh:mm:ss tt EST"
- Filter tables MUST be separated by 2 blank rows (current row + 3 for next table start)
- Columns MUST auto-fit with 15% additional padding (width * 1.15)
- The sheet MUST be password-protected using password from
ExcelExportOptions.CriteriaSheetPassword - Filter tables MUST use the Light18 table style (
XLTableTheme.TableStyleLight18)
Scenario: Document search with all filters populated
- WHEN generating criteria sheet for a search with all filter types populated
- THEN the system creates tables for each filter type in order: Timespan, Work Order, Item Number, Profit Center, Work Center, Component Lot, Operator, Item/Operation/MIS
Scenario: Document search with empty filters
- WHEN generating criteria sheet for a search with empty filter collections
- THEN the system still creates empty tables with headers for each filter type
Scenario: Format ExtractMisData indicator
- WHEN ExtractMisData is true
- THEN the system displays "YES" in the Extract MIS data row
Scenario: Format ExtractMisData indicator negative
- WHEN ExtractMisData is false
- THEN the system displays "NO" in the Extract MIS data row
Requirement: Search Results Sheet Generation
The system SHALL generate a "Search Results" sheet containing work order search results with standardized columns.
Inputs
List<SearchResult>containing work order data with:- Work order identifiers (number, branch code, lot number)
- Item information (number, planning family, stocking type)
- Quantities (order, held, scrapped, shipped)
- Operation details (step branch, number, description, function description)
- Timestamps (step update, status update)
- Status information (code, description)
- Inclusion reason (computed from flags)
Outputs
- Worksheet named "Search Results" with:
- 19 columns in defined order
- Data formatted as Excel table named "Search_Results"
- Light18 table style applied
- No worksheet protection (attribute-driven path)
Column Definitions
| Order | Header | Data Type | Format |
|---|---|---|---|
| 10 | Work Order Number | long | Standard |
| 20 | Work Order Branch Code | string | Standard |
| 30 | Lot Number | string | Standard |
| 40 | Item Number | string | Standard |
| 50 | Planning Family | string | Standard |
| 55 | Stocking Type | string | Standard |
| 60 | Order Quantity | decimal | Standard |
| 70 | Held Quantity | decimal | Standard |
| 80 | Scrapped Quantity | decimal | Standard |
| 90 | Shipped Quantity | decimal | Standard |
| 100 | Operation Step Branch Code | string | Standard |
| 110 | Operation Step | decimal | Standard |
| 120 | Operation Step Description | string | Standard |
| 130 | Function Operation Description | string | Standard |
| 140 | Operation Step Update Timestamp | DateTime | [$-409]m/d/yy h:mm AM/PM;@ |
| 150 | Status Code | string | Standard |
| 160 | Status Description | string | Standard |
| 170 | Status Update Timestamp | DateTime? | [$-409]MM/dd/yyyy;@ |
| 180 | Inclusion Reason | string (computed) | Standard |
| 190 | (Additional column per legacy code) | - | Standard |
Business Rules
- Columns MUST auto-fit with 30% additional padding (width * 1.3)
- The table MUST NOT show totals row
- Timestamp columns MUST use the defined Excel number formats for proper date/time display
- The Inclusion Reason column MUST compute values from boolean flags (ManuallySpecified, Flagged, CARDEX, PartsList, SplitOrder)
Scenario: Format inclusion reason for manually specified
- WHEN a result has ManuallySpecified = true
- THEN the Inclusion Reason displays "ManuallySpecified"
Scenario: Format inclusion reason for flagged
- WHEN a result has Flagged = true (and ManuallySpecified = false)
- THEN the Inclusion Reason displays "Flagged"
Scenario: Format inclusion reason for CARDEX only
- WHEN a result has CARDEX = true and PartsList = false
- THEN the Inclusion Reason displays "ComponentUsage (CARDEX)"
Scenario: Format inclusion reason for PartsList only
- WHEN a result has PartsList = true and CARDEX = false
- THEN the Inclusion Reason displays "ComponentUsage (Parts List)"
Scenario: Format inclusion reason for CARDEX and parts list
- WHEN a result has both CARDEX = true and PartsList = true
- THEN the Inclusion Reason displays "ComponentUsage (CARDEX + Parts List)"
Scenario: Format inclusion reason for split order
- WHEN a result has only SplitOrder = true
- THEN the Inclusion Reason displays "Split order"
Scenario: Format inclusion reason unknown
- WHEN a result has no matching boolean flags
- THEN the Inclusion Reason displays "UNKNOWN"
Requirement: MIS Info Sheet Generation
The system SHALL generate a "MIS Info" sheet containing Manufacturing Instruction Sheet data when enabled.
Inputs
List<MisSearchResult>containing:- Item identification (number, description)
- MIS metadata (number, revision, status, release date)
- Sequence/step numbers (MIS job step, job step, matched)
- Match indicators (RoutingMatch, MasterMatch)
- Description fields (function operation, test description)
- Sampling information (type, value)
- Long text fields (tools/gauges, work instructions)
Outputs
- Worksheet named "MIS Info" with:
- 19 columns in defined order
- Data formatted as Excel table named "MIS_Info"
- Light18 table style applied
- Specific columns with text wrapping and fixed width
Column Definitions
| Order | Header | Format | Width |
|---|---|---|---|
| 10 | Item Number | Standard | Auto |
| 20 | MIS Job Step Sequence Number | Standard | Auto |
| 30 | MIS Number | Standard | Auto |
| 40 | MIS Revision | Standard | Auto |
| 50 | Item Description | Standard | Auto |
| 60 | MIS Release Status | Standard | Auto |
| 70 | MIS Release Date | Timestamp | Auto |
| 80 | Branch Code | Standard | Auto |
| 90 | Job Step Sequence Number | Standard | Auto |
| 100 | Matched Sequence Number | Standard | Auto |
| 110 | Matched to F3112Z1? | Standard | Auto |
| 120 | Matched to F3003? | Standard | Auto |
| 130 | Function Operation Description | Standard | Auto |
| 140 | Char Number | Standard | Auto |
| 150 | Test Description | Standard | 65 (wrapped) |
| 160 | Sampling Type | Standard | Auto |
| 170 | Sampling Value | Standard | Auto |
| 180 | Tools & Gauges | Standard | 65 (wrapped) |
| 190 | Work Instructions | Standard | 65 (wrapped) |
Business Rules
- Columns with long text (Test Description, Tools & Gauges, Work Instructions) MUST have WrapText enabled and fixed width of 65
- Auto-width columns MUST apply 30% additional padding
- Wrapped columns MUST NOT be auto-fitted
Scenario: Generate MIS sheet with wrapped columns
- WHEN generating MIS Info sheet with data
- THEN Test Description, Tools & Gauges, and Work Instructions columns have fixed 65-character width with text wrapping enabled
Scenario: Handle null MIS results
- WHEN MisResults is null
- THEN the system skips MIS Info sheet generation entirely (returns early)
Scenario: Format boolean match indicators
- WHEN RoutingMatch or MasterMatch values are written
- THEN they display as "True" or "False" text values
Requirement: Investigation Sheet Generation
The system SHALL generate an "Investigation" sheet containing router mismatch data for analysis.
Inputs
List<MisNonMatchSearchResult>containing:- Work center and order identification
- Job step details (number, description, dates)
- Function and routing information
- Item details (number, description)
- Match indicators (WasJobStepAdded, MatchedJobStepNumber)
Outputs
- Worksheet named "Investigation" with:
- 12 columns in defined order
- Data formatted as Excel table named "Investigation"
- Light18 table style applied
Column Definitions
| Order | Header | Format |
|---|---|---|
| 10 | Work Center Code | Standard |
| 20 | Work Order Number | Standard |
| 30 | Work Order Start Date | [$-409]MM/dd/yyyy;@ |
| 40 | Job Step Number | Standard |
| 50 | Function Operation Description | Standard |
| 60 | Job Step End Date | [$-409]MM/dd/yyyy;@ |
| 70 | Function Code | Standard |
| 75 | Was Job Step Added? | Standard (boolean) |
| 76 | Matched Job Step Number | Standard |
| 80 | Item Number | Standard |
| 90 | Item Description | Standard |
| 100 | Routing Type | Standard |
Business Rules
- Date columns MUST use
DATE_FORMAT([$-409]MM/dd/yyyy;@) - All columns MUST auto-fit with 30% additional padding
Scenario: Handle null mismatch results
- WHEN MisNonMatchResults is null
- THEN the system skips Investigation sheet generation entirely
Scenario: Format date columns
- WHEN generating Investigation sheet
- THEN Work Order Start Date and Job Step End Date columns use
[$-409]MM/dd/yyyy;@number format
Requirement: Worksheet Protection
The system SHALL apply password-based protection to worksheets with configurable allowed operations.
Inputs
- Worksheet to protect
- Protection password from
IOptions<ExcelExportOptions> - Editable range definition (cells beyond data area)
Outputs
- Protected worksheet with:
- Locked data cells
- Unlocked extension area for user additions
- Specific operations allowed/disallowed
Business Rules
- Protection passwords MUST be loaded from
ExcelExportOptionsconfiguration - Protected ranges MUST allow the following operations via
IXLSheetProtection:- Delete columns: YES
- Delete rows: NO
- Auto filter: YES
- Format cells: YES
- Format columns: YES
- Format rows: YES
- Select locked cells: YES
- Select unlocked cells: YES
- Edit objects: YES
- Sort: YES
- Unprotected area MUST extend 1000 rows and columns beyond data range
ClosedXML Protection Example
var protection = worksheet.Protect(options.Value.DataSheetPassword);
protection.AllowElement(XLSheetProtectionElements.DeleteColumns);
protection.AllowElement(XLSheetProtectionElements.AutoFilter);
protection.AllowElement(XLSheetProtectionElements.FormatCells);
protection.AllowElement(XLSheetProtectionElements.FormatColumns);
protection.AllowElement(XLSheetProtectionElements.FormatRows);
protection.AllowElement(XLSheetProtectionElements.SelectLockedCells);
protection.AllowElement(XLSheetProtectionElements.SelectUnlockedCells);
protection.AllowElement(XLSheetProtectionElements.EditObjects);
protection.AllowElement(XLSheetProtectionElements.Sort);
Scenario: Protect search criteria sheet
- WHEN generating Search Criteria sheet
- THEN sheet is protected using
ExcelExportOptions.CriteriaSheetPassword
Scenario: Apply consistent protection settings
- WHEN protecting any data worksheet
- THEN all boolean protection flags match the defined allowed operations
Scenario: Allow filtering and sorting
- WHEN user opens protected worksheet
- THEN they can filter and sort data without entering password
Requirement: Attribute-Driven Column Configuration
The system SHALL use C# attributes to define column metadata for automatic table generation.
Inputs
- Data model classes decorated with
OutputTableAttributeandOutputColumnAttribute - Properties marked with
OutputColumnAttributedefining column order, header, format, width, and wrap settings
Outputs
- Dynamically generated Excel tables based on attribute configuration
Property Access Strategy
- The system SHALL use native .NET reflection (
PropertyInfo.GetValue()) for property access - Source generators MAY be used as future optimization for compile-time property mapping
OutputColumnAttribute Properties
| Property | Type | Default | Purpose |
|---|---|---|---|
| Order | int | - | Column sort order |
| HeaderText | string | - | Column header display text |
| Format | string | "@" (text) | Excel number format |
| AutoWidth | bool | true | Enable auto-fit |
| Width | double | - | Fixed width (when AutoWidth=false) |
| WrapText | bool | false | Enable text wrapping |
OutputTableAttribute Properties
| Property | Type | Purpose |
|---|---|---|
| TabName | string | Worksheet tab name |
| TableName | string | Excel table name |
| ShowHeader | bool | Show merged header row above table |
Standard Formats
| Constant | Value | Usage |
|---|---|---|
| STD_FORMAT | "@" | Text format (default) |
| DATE_FORMAT | "[$-409]MM/dd/yyyy;@" | Date only |
| TIMESTAMP_FORMAT | "[$-409]m/d/yy h:mm AM/PM;@" | Date and time |
| WRAPPED_COLUMN_WIDTH | 65 | Fixed width for wrapped columns |
Scenario: Generate table from decorated model
- WHEN LoadTab is called with a model type having OutputTableAttribute
- THEN worksheet name and table name are derived from attribute values
Scenario: Order columns by attribute
- WHEN generating table from model with OutputColumnAttribute
- THEN columns appear in Order property sequence, with ties broken alphabetically by property name
Scenario: Apply custom format to column
- WHEN a property has OutputColumnAttribute with Format specified
- THEN that format is applied to the entire column's number format
Scenario: Apply wrapped text configuration
- WHEN a property has WrapText=true and AutoWidth=false
- THEN column has text wrapping enabled and uses fixed Width value
Requirement: Data Entry Template Generation
The system SHALL generate simple Excel templates for bulk data entry of filter values.
Inputs
- Source data collection (optional, for pre-population)
- Header text (single column) or header array (multi-column)
Outputs
- Single-sheet workbook named "Data Entry Template"
- Header row with standard formatting
- Optional pre-populated data rows
- All columns formatted as text ("@")
Business Rules
- Header row MUST be bold, centered, with Gainsboro background via
XLColor.Gainsboro - Single-column templates MUST use 45-character width
- Multi-column templates MUST use 65-character width per column
- All cells MUST use text format to preserve leading zeros in item/lot numbers
Scenario: Generate empty single-column template
- WHEN Generate is called with null sourceData
- THEN template contains only header row with no data
Scenario: Generate pre-populated template
- WHEN Generate is called with sourceData containing values
- THEN data rows are populated starting at row 2
Scenario: Generate multi-column template
- WHEN Generate is called with object[][] sourceData and string[] headers
- THEN multiple columns are created with respective headers
Requirement: Header Cell Formatting
The system SHALL apply consistent header formatting across all worksheets.
Inputs
- Cell range to format
- Optional text value
- Optional merge flag
Outputs
- Formatted header cell(s) with:
- Horizontal center alignment
- Bold font
- Solid Gainsboro (light gray) background fill via
XLColor.Gainsboro
ClosedXML Formatting Example
var cell = worksheet.Cell(row, column);
cell.Value = headerText;
cell.Style.Font.Bold = true;
cell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
cell.Style.Fill.BackgroundColor = XLColor.Gainsboro;
Business Rules
- Header format MUST be applied to:
- All table header rows
- Label cells in criteria sheet (column 1)
- Filter table section headers
- Merge flag MUST only be set for multi-cell ranges using
IXLRange.Merge() - Text value MUST be written when provided
Scenario: Format single header cell
- WHEN ApplyHeaderFormat is called on single cell with text
- THEN cell has bold font, center alignment, Gainsboro background, and displays provided text
Scenario: Format merged header range
- WHEN ApplyHeaderFormat is called on multi-cell range with merge=true
- THEN cells are merged and formatted as single header
Requirement: Async Generation Pattern
The system SHALL support async/await patterns for export generation.
Business Rules
- The
GenerateAsyncmethod MUST returnTask<byte[]> - Since ClosedXML's
SaveAstoMemoryStreamis synchronous, the implementation MAY wrap the CPU-bound work inTask.Run()to avoid blocking - The method MUST accept
CancellationTokenfor cancellation support - For very large exports, future versions MAY implement
Stream-based output
Implementation Example
public async Task<byte[]> GenerateAsync(SearchModel search, CancellationToken cancellationToken = default)
{
using var scope = _logger.BeginScope(new Dictionary<string, object>
{
["SearchId"] = search.Id,
["SearchName"] = search.Name
});
_logger.LogInformation("Starting Excel export generation");
// ClosedXML operations are synchronous, wrap in Task.Run for non-blocking
return await Task.Run(() =>
{
using var workbook = new XLWorkbook();
// ... build sheets ...
using var stream = new MemoryStream();
workbook.SaveAs(stream);
return stream.ToArray();
}, cancellationToken);
}
Scenario: Generate export asynchronously
- WHEN
GenerateAsyncis called - THEN the method returns a
Task<byte[]>that completes when workbook generation finishes
Scenario: Support cancellation
- WHEN
GenerateAsyncis called with aCancellationTokenthat is cancelled - THEN the operation throws
OperationCanceledException
Migration Notes
| Legacy Pattern | New Pattern | Rationale |
|---|---|---|
| EPPlus 4.x (LGPL) | ClosedXML (MIT license) | EPPlus 7+ requires commercial license; ClosedXML is fully free and MIT licensed |
System.Drawing.Color |
XLColor |
ClosedXML uses its own color type (XLColor.Gainsboro, etc.) |
ExcelPackage |
XLWorkbook |
ClosedXML workbook class |
ExcelWorksheet |
IXLWorksheet |
ClosedXML worksheet interface |
ExcelRange |
IXLRange or IXLCell |
ClosedXML range/cell interfaces |
| Fasterflect reflection | Native reflection or source generators | Reduce dependencies; native PropertyInfo.GetValue() is sufficient; source generators available for future optimization |
| Extension methods on EPPlus types | Service class with IExcelExportService interface |
Enable testing and alternative implementations |
Static ExcelWriter.Generate() |
Injectable IExcelExportService |
Dependency injection for testability and configuration |
| Hard-coded passwords | IOptions<ExcelExportOptions> configuration |
Move protection passwords to configuration for security and flexibility |
| Byte array return | byte[] and Stream |
Support both: GenerateAsync returns byte[], GenerateToStreamAsync writes to Stream for large exports |
| Synchronous generation | Async wrapping via Task.Run() |
Support async patterns; ClosedXML SaveAs is sync, wrap for non-blocking |
| TableStyles enum | XLTableTheme |
ClosedXML table themes (e.g., XLTableTheme.TableStyleLight18) |
| NLog static logging | ILogger<T> injected + BeginScope() |
Modern structured logging with contextual scopes |
Resolved Design Decisions
-
Library Selection: Use ClosedXML (MIT license) - fully free for commercial use, similar API to EPPlus, active maintenance.
-
Password Protection: Move to
IOptions<ExcelExportOptions>configuration. Default values preserved for backward compatibility but can be overridden via appsettings.json. -
Large Export Handling: Implement streaming architecture for memory-efficient large exports.
- The system SHALL support
Stream-based output for large workbooks to avoid memory pressure - The system SHALL provide both
GenerateAsync(returnsbyte[]) andGenerateToStreamAsync(writes toStream) methods - For exports exceeding a configurable row threshold, the streaming approach SHALL be preferred
- The system SHALL support
-
Async Support:
GenerateAsyncmethod wraps synchronous ClosedXML operations inTask.Run()to avoid blocking. -
Format Compatibility: Maintain locale ID 409 (US English) for timestamp formats. International configuration deferred to future version.
-
Template Generator: Retain
ExcelTemplateGeneratorfunctionality for bulk data entry via the Blazor UI.
Codex Review Findings (Addressed)
The following inaccuracies were identified during review and have been addressed in this specification:
-
Table Style and Protection: CORRECTED - Spec now states data sheets use Light18 style (via
XLTableTheme.TableStyleLight18) and protection is applied only to criteria sheet by default. Data sheets generated via attribute-drivenLoadTabdo not apply protection. -
Column Counts Corrected:
- Search Results: CORRECTED to 19 columns (was incorrectly 18)
- Investigation: CORRECTED to 12 columns (was incorrectly 11)
-
Investigation Date Format: CORRECTED - Spec now states
DATE_FORMAT([$-409]MM/dd/yyyy;@) is used, not "m/d/yyyy". -
Inclusion Reason Scenarios Complete: ADDED - Scenarios now include CARDEX-only, PartsList-only, and UNKNOWN cases.
-
Null List Handling: CLARIFIED - Spec now explicitly states null checks are required before calling sheet generation methods. Implementation MUST check for null before generating MIS Info and Investigation sheets.
-
Criteria Table Spacing: CORRECTED - Spec now states "2 blank rows" between filter tables (current row + 3 for next table start).