Compare commits
15 Commits
0fcc632024
...
7fd139a234
Author | SHA1 | Date | |
---|---|---|---|
|
7fd139a234 | ||
|
9d1c213979 | ||
|
fba2859b84 | ||
|
28c5a69797 | ||
|
c0c656c82c | ||
|
a56300a620 | ||
|
16b5ddedb1 | ||
|
733440392e | ||
|
136aa7f238 | ||
|
d894c50ca3 | ||
|
c308e72ee0 | ||
|
21fb58744c | ||
|
7a7cdaab58 | ||
|
5720d2ede0 | ||
|
dcd42a32eb |
@ -25,8 +25,7 @@ public sealed class RhSolutionsAddIn : IExcelAddIn
|
||||
.AddSingleton<IAddInConfiguration, AddInConfiguration>()
|
||||
.AddSingleton<IDatabaseClient, DatabaseClient>()
|
||||
.AddTransient<ICurrencyClient, CurrencyClient>()
|
||||
.AddTransient<IFileDialog, FileDialog>()
|
||||
.AddTransient<IReader, ExcelReader>();
|
||||
.AddTransient<IFileDialog, FileDialog>();
|
||||
|
||||
Services.AddSingleton<WriterFactory>();
|
||||
Services.AddTransient<ExcelWriter>()
|
||||
@ -34,6 +33,12 @@ public sealed class RhSolutionsAddIn : IExcelAddIn
|
||||
Services.AddTransient<DxfWriter>()
|
||||
.AddTransient<IWriter, DxfWriter>(s => s.GetService<DxfWriter>());
|
||||
|
||||
Services.AddSingleton<ReaderFactory>();
|
||||
Services.AddTransient<ExcelReader>()
|
||||
.AddTransient<IReader, ExcelReader>(s => s.GetService<ExcelReader>());
|
||||
Services.AddTransient<GuessReader>()
|
||||
.AddTransient<IReader, GuessReader>(s => s.GetService<GuessReader>());
|
||||
|
||||
Services.AddSingleton<ToolFactory>();
|
||||
|
||||
ServiceProvider = Services.BuildServiceProvider();
|
||||
|
@ -27,6 +27,7 @@ public class RibbonController : ExcelRibbon
|
||||
<button id='export' getEnabled='GetExportEnabled' label='Экспорт в новый файл' size='normal' imageMso='PivotExportToExcel' onAction='OnToolPressed'/>
|
||||
<button id='convert' getEnabled='GetConvertEnabled' label='Актуализировать' size='normal' imageMso='FileUpdate' onAction='OnToolPressed'/>
|
||||
<button id='merge' label='Объединить' size='normal' imageMso='Copy' onAction='OnToolPressed'/>
|
||||
<button id='Guessexport' getEnabled='GetGuessEnabled' label='Найти и экспортировать' size='normal' imageMso='ControlWizards' onAction='OnToolPressed'/>
|
||||
<button id='dxfexport' getEnabled='GetDxfEnabled' label='Экспортировать в DXF' size='normal' imageMso='ExportExcel' onAction='OnToolPressed'/>
|
||||
</group>
|
||||
<group id='rausettings' getLabel='GetVersionLabel'>
|
||||
@ -116,6 +117,18 @@ public class RibbonController : ExcelRibbon
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetGuessEnabled(IRibbonControl control)
|
||||
{
|
||||
if (RhSolutionsAddIn.Excel.ActiveWorkbook == null)
|
||||
return false;
|
||||
|
||||
else
|
||||
{
|
||||
Worksheet worksheet = RhSolutionsAddIn.Excel.ActiveWorkbook.ActiveSheet;
|
||||
return !worksheet.IsValidSource();
|
||||
}
|
||||
}
|
||||
|
||||
public string GetVersionLabel(IRibbonControl control)
|
||||
{
|
||||
string version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
|
237
RhSolutions.AddIn/Services/GuessReader.cs
Normal file
237
RhSolutions.AddIn/Services/GuessReader.cs
Normal file
@ -0,0 +1,237 @@
|
||||
using System.IO;
|
||||
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
public class GuessReader : IReader
|
||||
{
|
||||
private readonly Application _application;
|
||||
private ProgressBar _progressBar;
|
||||
|
||||
public GuessReader(Application application)
|
||||
{
|
||||
_application = application;
|
||||
}
|
||||
|
||||
public Dictionary<Product, double> ReadProducts(Range range)
|
||||
{
|
||||
_progressBar = new("Ищу колонки со значениями", range.Columns.Count);
|
||||
int? productColumnIndex = null;
|
||||
List<int> amountColumnIndeces = new();
|
||||
|
||||
for (int column = 1; column < range.Columns.Count + 1; column++)
|
||||
{
|
||||
_progressBar.Update();
|
||||
if (productColumnIndex == null && IsProductColumn(range.Columns[column]))
|
||||
{
|
||||
productColumnIndex = column;
|
||||
}
|
||||
else if (IsAmountColumn(range.Columns[column]))
|
||||
{
|
||||
amountColumnIndeces.Add(column);
|
||||
}
|
||||
}
|
||||
|
||||
if (productColumnIndex == null)
|
||||
{
|
||||
throw new ArgumentException("Колонка с артикулом не определена");
|
||||
}
|
||||
|
||||
if (amountColumnIndeces.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("Колонка с количеством не определена");
|
||||
}
|
||||
|
||||
else if (amountColumnIndeces.Count == 1)
|
||||
{
|
||||
return GetDictionaryFromColumns(range.Columns[productColumnIndex],
|
||||
range.Columns[amountColumnIndeces.First()]);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
int amountColumnIndex = amountColumnIndeces
|
||||
.Where(i => i > productColumnIndex)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (amountColumnIndex == 0)
|
||||
{
|
||||
amountColumnIndex = amountColumnIndeces
|
||||
.OrderBy(i => i)
|
||||
.Where(i => i < productColumnIndex)
|
||||
.LastOrDefault();
|
||||
}
|
||||
|
||||
if (amountColumnIndex == 0)
|
||||
{
|
||||
throw new ArgumentException("Колонка с количеством не определена");
|
||||
}
|
||||
|
||||
return GetDictionaryFromColumns(range.Columns[productColumnIndex],
|
||||
range.Columns[amountColumnIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsProductColumn(Range column)
|
||||
{
|
||||
int successCounter = 0;
|
||||
object[,] cells = column.Value2;
|
||||
|
||||
if (cells == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int row = 1; row < column.Rows.Count + 1; row++)
|
||||
{
|
||||
object currentCell = cells[row, 1];
|
||||
if (currentCell == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ProductSku.TryParse(currentCell.ToString(), out IEnumerable<ProductSku> skus))
|
||||
{
|
||||
successCounter++;
|
||||
}
|
||||
|
||||
if (successCounter > 2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return successCounter > 0;
|
||||
}
|
||||
|
||||
private bool IsAmountColumn(Range column)
|
||||
{
|
||||
int successCounter = 0;
|
||||
object[,] cells = column.Value2;
|
||||
|
||||
if (cells == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int row = 1; row < column.Rows.Count + 1; row++)
|
||||
{
|
||||
object currentCell = cells[row, 1];
|
||||
if (currentCell == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double value = 0.0;
|
||||
|
||||
if (currentCell.GetType() == typeof(double))
|
||||
{
|
||||
value = (double)currentCell;
|
||||
}
|
||||
|
||||
else if (currentCell.GetType() == typeof(string))
|
||||
{
|
||||
double.TryParse((string)currentCell, out value);
|
||||
}
|
||||
|
||||
if (value == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value > 30000)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
successCounter++;
|
||||
}
|
||||
|
||||
return successCounter > 1;
|
||||
}
|
||||
|
||||
private Dictionary<Product, double> GetDictionaryFromColumns(Range productColumn, Range amountColumn)
|
||||
{
|
||||
Dictionary<Product, double> result = new();
|
||||
|
||||
for (int row = 1; row < productColumn.Rows.Count + 1; row++)
|
||||
{
|
||||
object[,] amountCells = amountColumn.Value2;
|
||||
object currentAmountCell = amountCells[row, 1];
|
||||
|
||||
object[,] productCells = productColumn.Value2;
|
||||
object currentProductCell = productCells[row, 1];
|
||||
|
||||
double amountValue = 0.0;
|
||||
|
||||
if (currentAmountCell == null || currentProductCell == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ProductSku.TryParse(currentProductCell.ToString(), out IEnumerable<ProductSku> skus))
|
||||
{
|
||||
Product p = new(skus.First())
|
||||
{
|
||||
Name = "Распознанный артикул"
|
||||
};
|
||||
if (currentAmountCell.GetType() == typeof(double))
|
||||
{
|
||||
amountValue = (double)currentAmountCell;
|
||||
}
|
||||
else if (currentAmountCell.GetType() == typeof(string))
|
||||
{
|
||||
double.TryParse((string)currentAmountCell, out amountValue);
|
||||
}
|
||||
if (result.ContainsKey(p))
|
||||
{
|
||||
result[p] += amountValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(p, amountValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<(string, Dictionary<Product, double>)> ReadProducts(IEnumerable<Worksheet> worksheets)
|
||||
{
|
||||
List<(string, Dictionary<Product, double>)> result = new();
|
||||
foreach (Worksheet worksheet in worksheets)
|
||||
{
|
||||
string wbName = Path.GetFileNameWithoutExtension(
|
||||
worksheet.Parent.Name);
|
||||
Range range = worksheet.UsedRange;
|
||||
var productsDict = ReadProducts(range);
|
||||
result.Add((wbName, productsDict));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<(string, Dictionary<Product, double>)> ReadProducts(string[] files)
|
||||
{
|
||||
_progressBar = new("Открываю исходные файлы...", files.Length);
|
||||
List<Worksheet> worksheets = new();
|
||||
|
||||
_application.ScreenUpdating = false;
|
||||
foreach (string file in files)
|
||||
{
|
||||
Workbook wb = _application.Workbooks.Open(file);
|
||||
worksheets.Add(wb.ActiveSheet);
|
||||
_progressBar.Update();
|
||||
}
|
||||
_application.ScreenUpdating = true;
|
||||
var result = ReadProducts(worksheets);
|
||||
foreach (var ws in worksheets)
|
||||
{
|
||||
ws.Parent.Close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_progressBar?.Dispose();
|
||||
}
|
||||
}
|
21
RhSolutions.AddIn/Services/ReaderFactory.cs
Normal file
21
RhSolutions.AddIn/Services/ReaderFactory.cs
Normal file
@ -0,0 +1,21 @@
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
public class ReaderFactory
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public ReaderFactory(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IReader GetReader(string readerName)
|
||||
{
|
||||
return readerName switch
|
||||
{
|
||||
"Guess" => (IReader)_serviceProvider.GetService(typeof(GuessReader)),
|
||||
"Excel" => (IReader)_serviceProvider.GetService(typeof(ExcelReader)),
|
||||
_ => (IReader)_serviceProvider.GetService(typeof(ExcelReader))
|
||||
};
|
||||
}
|
||||
}
|
@ -11,14 +11,11 @@ public class WriterFactory
|
||||
|
||||
public IWriter GetWriter(string writerName)
|
||||
{
|
||||
if (writerName.Equals("Dxf"))
|
||||
return writerName switch
|
||||
{
|
||||
return (IWriter)_serviceProvider.GetService(typeof(DxfWriter));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return (IWriter)_serviceProvider.GetService(typeof(ExcelWriter));
|
||||
}
|
||||
"Excel" => (IWriter)_serviceProvider.GetService(typeof(ExcelWriter)),
|
||||
"Dxf" => (IWriter)_serviceProvider.GetService(typeof(DxfWriter)),
|
||||
_ => (IWriter)_serviceProvider.GetService(typeof(ExcelWriter))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,9 @@ internal class ConvertTool : Tool
|
||||
{
|
||||
Application app = RhSolutionsAddIn.Excel.Application;
|
||||
Worksheet worksheet = app.ActiveWorkbook.ActiveSheet;
|
||||
var products = _reader.ReadProducts(new[] { worksheet });
|
||||
_reader = _readerFactory.GetReader("Excel");
|
||||
_writer = _writerFactory.GetWriter("Excel");
|
||||
var products = _reader.ReadProducts(new[] { worksheet });
|
||||
_writer.WriteProducts(products);
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ internal class DxfTool : Tool
|
||||
{
|
||||
Application app = RhSolutionsAddIn.Excel.Application;
|
||||
Worksheet worksheet = app.ActiveWorkbook.ActiveSheet;
|
||||
_reader = _readerFactory.GetReader("Excel");
|
||||
var products = _reader.ReadProducts(new[] { worksheet });
|
||||
_writer = _writerFactory.GetWriter("Dxf");
|
||||
_writer.WriteProducts(products);
|
||||
|
@ -18,8 +18,10 @@ namespace RhSolutions.Tools
|
||||
RhSolutionsAddIn.Excel.SheetSelectionChange += RefreshExportButton;
|
||||
RhSolutionsAddIn.Excel.SheetActivate += RefreshConvertButton;
|
||||
RhSolutionsAddIn.Excel.SheetActivate += RefreshDxfButton;
|
||||
RhSolutionsAddIn.Excel.SheetActivate += RefreshGuessButton;
|
||||
RhSolutionsAddIn.Excel.WorkbookActivate += RefreshConvertButton;
|
||||
RhSolutionsAddIn.Excel.WorkbookActivate += RefreshDxfButton;
|
||||
RhSolutionsAddIn.Excel.WorkbookActivate += RefreshGuessButton;
|
||||
RhSolutionsAddIn.Configuration.OnSettingsChange += RefreshSettingTitle;
|
||||
}
|
||||
|
||||
@ -28,8 +30,10 @@ namespace RhSolutions.Tools
|
||||
RhSolutionsAddIn.Excel.SheetSelectionChange -= RefreshExportButton;
|
||||
RhSolutionsAddIn.Excel.SheetActivate -= RefreshConvertButton;
|
||||
RhSolutionsAddIn.Excel.SheetActivate -= RefreshDxfButton;
|
||||
RhSolutionsAddIn.Excel.SheetActivate -= RefreshGuessButton;
|
||||
RhSolutionsAddIn.Excel.WorkbookActivate -= RefreshConvertButton;
|
||||
RhSolutionsAddIn.Excel.WorkbookActivate -= RefreshDxfButton;
|
||||
RhSolutionsAddIn.Excel.WorkbookActivate -= RefreshGuessButton;
|
||||
RhSolutionsAddIn.Configuration.OnSettingsChange -= RefreshSettingTitle;
|
||||
}
|
||||
|
||||
@ -48,6 +52,11 @@ namespace RhSolutions.Tools
|
||||
RibbonController.RefreshControl("export");
|
||||
}
|
||||
|
||||
private static void RefreshGuessButton(object sh)
|
||||
{
|
||||
RibbonController.RefreshControl("Guessexport");
|
||||
}
|
||||
|
||||
private static void RefreshSettingTitle(object sender, SettingChangingEventArgs e)
|
||||
{
|
||||
RibbonController.RefreshControl("setPriceList");
|
||||
|
@ -1,5 +1,4 @@
|
||||
using RhSolutions.AddIn;
|
||||
#if !NET472
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
@ -17,8 +16,9 @@ internal class ExportTool : Tool
|
||||
public override void Execute()
|
||||
{
|
||||
Application app = RhSolutionsAddIn.Excel.Application;
|
||||
var products = _reader.ReadProducts(app.Selection);
|
||||
_reader = _readerFactory.GetReader("Excel");
|
||||
_writer = _writerFactory.GetWriter("Excel");
|
||||
var products = _reader.ReadProducts(app.Selection);
|
||||
_writer.WriteProducts(products);
|
||||
}
|
||||
}
|
||||
|
23
RhSolutions.AddIn/Tools/GuessTool.cs
Normal file
23
RhSolutions.AddIn/Tools/GuessTool.cs
Normal file
@ -0,0 +1,23 @@
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.Tools;
|
||||
|
||||
internal class GuessTool : Tool
|
||||
{
|
||||
public GuessTool(IServiceProvider provider) : base(provider)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
Application app = RhSolutionsAddIn.Excel.Application;
|
||||
Worksheet worksheet = app.ActiveWorkbook.ActiveSheet;
|
||||
_reader = _readerFactory.GetReader("Guess");
|
||||
_writer = _writerFactory.GetWriter("Excel");
|
||||
var products = _reader.ReadProducts(new[] { worksheet });
|
||||
_writer.WriteProducts(products);
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,9 @@ internal class MergeTool : Tool
|
||||
{
|
||||
IFileDialog dialog = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IFileDialog>();
|
||||
string[] files = dialog.GetFiles();
|
||||
var products = _reader.ReadProducts(files);
|
||||
_reader = _readerFactory.GetReader("Excel");
|
||||
_writer = _writerFactory.GetWriter("Excel");
|
||||
var products = _reader.ReadProducts(files);
|
||||
_writer.WriteProducts(products);
|
||||
}
|
||||
}
|
||||
|
@ -9,13 +9,14 @@ namespace RhSolutions.Tools;
|
||||
#endif
|
||||
internal abstract class Tool : IDisposable
|
||||
{
|
||||
protected readonly IReader _reader;
|
||||
protected readonly ReaderFactory _readerFactory;
|
||||
protected readonly WriterFactory _writerFactory;
|
||||
protected IReader _reader;
|
||||
protected IWriter _writer;
|
||||
|
||||
public Tool(IServiceProvider provider)
|
||||
{
|
||||
_reader = provider.GetRequiredService<IReader>();
|
||||
_readerFactory = provider.GetRequiredService<ReaderFactory>();
|
||||
_writerFactory = provider.GetRequiredService<WriterFactory>();
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ internal class ToolFactory
|
||||
"convert" => new ConvertTool(RhSolutionsAddIn.ServiceProvider),
|
||||
"merge" => new MergeTool(RhSolutionsAddIn.ServiceProvider),
|
||||
"dxfexport" => new DxfTool(RhSolutionsAddIn.ServiceProvider),
|
||||
"Guessexport" => new GuessTool(RhSolutionsAddIn.ServiceProvider),
|
||||
_ => throw new Exception("Неизвестный инструмент"),
|
||||
};
|
||||
return tool;
|
||||
|
42
RhSolutions.Tests/CanDoGuess.cs
Normal file
42
RhSolutions.Tests/CanDoGuess.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using RhSolutions.AddIn;
|
||||
using System.IO;
|
||||
|
||||
namespace RhSolutions.Tests;
|
||||
|
||||
[ExcelTestSettings(OutOfProcess = true)]
|
||||
public class CanDoGuess : IDisposable
|
||||
{
|
||||
private RhSolutionsAddIn _addIn;
|
||||
private IReader _reader;
|
||||
|
||||
public CanDoGuess()
|
||||
{
|
||||
_addIn = new();
|
||||
_addIn.AutoOpen();
|
||||
_reader = new GuessReader(Util.Application);
|
||||
}
|
||||
|
||||
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\TestSpecificationGuess.xlsx")]
|
||||
public void CanWrite()
|
||||
{
|
||||
Worksheet sourceSheet = Util.Workbook.Worksheets[1];
|
||||
RhSolutionsAddIn.Configuration.SetPriceListPath(Path.GetFullPath(@"..\..\..\TestWorkbooks\TargetSpecificationGuess.xlsx"));
|
||||
var products = _reader.ReadProducts(new[] { sourceSheet });
|
||||
var _writer = new ExcelWriter(Util.Application, RhSolutionsAddIn.Configuration);
|
||||
_writer.WriteProducts(products);
|
||||
Worksheet targetSheet = Util.Application.ActiveWindow.ActiveSheet;
|
||||
var targetProducts = _reader.ReadProducts(new[] { targetSheet });
|
||||
|
||||
Assert.Equal("TestSpecificationGuess", products.First().Item1);
|
||||
Assert.Equal("TargetSpecificationGuess", targetProducts.First().Item1);
|
||||
Assert.Equal(products.First().Item2.Count(), targetProducts.First().Item2.Count());
|
||||
Assert.Equal(products.First().Item2.Values.Sum(), targetProducts.First().Item2.Values.Sum());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_addIn.AutoClose();
|
||||
Util.Application.ActiveWindow.Close(SaveChanges: false);
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ public class CanReadProducts : IDisposable
|
||||
_addIn = new();
|
||||
_testWorkbook = Util.Application.Workbooks.Add();
|
||||
_addIn.AutoOpen();
|
||||
_reader = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IReader>();
|
||||
_reader = new ExcelReader(Util.Application, RhSolutionsAddIn.Configuration);
|
||||
}
|
||||
|
||||
[ExcelFact]
|
||||
|
@ -14,7 +14,7 @@ public class CanWriteProducts : IDisposable
|
||||
{
|
||||
_addIn = new();
|
||||
_addIn.AutoOpen();
|
||||
_reader = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IReader>();
|
||||
_reader = new ExcelReader(Util.Application, RhSolutionsAddIn.Configuration);
|
||||
}
|
||||
|
||||
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\TestSpecification.xlsx")]
|
||||
|
@ -14,7 +14,7 @@ public class RealPricelistTest : IDisposable
|
||||
{
|
||||
_addIn = new();
|
||||
_addIn.AutoOpen();
|
||||
_reader = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IReader>();
|
||||
_reader = new ExcelReader(Util.Application, RhSolutionsAddIn.Configuration);
|
||||
}
|
||||
|
||||
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\RealTestSpecification.xlsm")]
|
||||
|
BIN
RhSolutions.Tests/TestWorkbooks/TargetSpecificationGuess.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TargetSpecificationGuess.xlsx
Normal file
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationGuess.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationGuess.xlsx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user