Compare commits

...

15 Commits

Author SHA1 Message Date
Sergey Chebotar
7fd139a234 Edit amount column looking up 2023-05-23 09:26:08 +03:00
Sergey Chebotar
9d1c213979 Add guess button enable and disable events 2023-05-23 07:55:58 +03:00
Sergey Chebotar
fba2859b84 Fix null cells reference 2023-05-23 07:55:14 +03:00
Sergey Chebotar
28c5a69797 Button change 2023-05-23 07:45:37 +03:00
Sergey Chebotar
c0c656c82c Fix null reference 2023-05-23 07:33:49 +03:00
Sergey Chebotar
a56300a620 Get writer before reading 2023-05-23 07:33:40 +03:00
Sergey Chebotar
16b5ddedb1 Rename Magic to Guess 2023-05-23 07:07:16 +03:00
Sergey Chebotar
733440392e Fix testing reader type 2023-05-23 07:01:33 +03:00
Sergey Chebotar
136aa7f238 Add Magic Tool 2023-05-23 06:54:37 +03:00
Sergey Chebotar
d894c50ca3 Add Magic button to Ribbon Controller 2023-05-23 06:53:20 +03:00
Sergey Chebotar
c308e72ee0 Add Magic Service 2023-05-23 06:52:52 +03:00
Sergey Chebotar
21fb58744c Implement Magic Reader 2023-05-22 10:24:18 +03:00
Sergey Chebotar
7a7cdaab58 Add Magic reader test 2023-05-22 08:05:44 +03:00
Sergey Chebotar
5720d2ede0 Add ReaderFactory 2023-05-22 07:39:48 +03:00
Sergey Chebotar
dcd42a32eb WriterFactory refactoring 2023-05-22 07:31:53 +03:00
19 changed files with 372 additions and 20 deletions

View File

@ -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();

View File

@ -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();

View 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();
}
}

View 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))
};
}
}

View File

@ -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))
};
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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");

View File

@ -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);
}
}

View 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);
}
}

View File

@ -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);
}
}

View File

@ -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>();
}

View File

@ -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;

View 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);
}
}

View File

@ -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]

View File

@ -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")]

View File

@ -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")]