DI Refactoring
This commit is contained in:
parent
448af8ecd7
commit
bfd7702939
@ -1,9 +1,15 @@
|
||||
namespace RhSolutions.AddIn;
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.AddIn;
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
public static class ResetBarFunction
|
||||
{
|
||||
[ExcelFunction]
|
||||
public static void _ResetStatusBar()
|
||||
public static void StatusBarReset()
|
||||
{
|
||||
RhSolutionsAddIn.Excel.StatusBar = false;
|
||||
}
|
||||
|
@ -1,8 +1,15 @@
|
||||
using ExcelDna.IntelliSense;
|
||||
using RhSolutions.Tools;
|
||||
using System.Net;
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.AddIn;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
public sealed class RhSolutionsAddIn : IExcelAddIn
|
||||
{
|
||||
public static Application Excel { get; private set; }
|
||||
@ -14,9 +21,12 @@ public sealed class RhSolutionsAddIn : IExcelAddIn
|
||||
IServiceCollection Services = new ServiceCollection();
|
||||
|
||||
Services.AddHttpClient()
|
||||
.AddSingleton((Application)ExcelDnaUtil.Application)
|
||||
.AddSingleton<IDatabaseClient, RhDatabaseClient>()
|
||||
.AddSingleton<IAddInConfiguration, AddInConfiguration>()
|
||||
.AddSingleton((Application)ExcelDnaUtil.Application);
|
||||
.AddSingleton<IAddInConfiguration, RhAddInConfiguration>()
|
||||
.AddTransient<IFileDialog, ExcelFileDialog>()
|
||||
.AddTransient<IExcelReader, RhExcelReader>()
|
||||
.AddTransient<IExcelWriter, RhExcelWriter>();
|
||||
|
||||
ServiceProvider = Services.BuildServiceProvider();
|
||||
Configuration = ServiceProvider.GetService<IAddInConfiguration>();
|
||||
|
@ -1,23 +1,29 @@
|
||||
namespace RhSolutions.AddIn;
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.AddIn;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
public class RhSolutionsFunction
|
||||
{
|
||||
[ExcelFunction(Description = "Распознать артикул и попробовать найти его в прайс-листе")]
|
||||
public static object RHSOLUTIONS([ExcelArgument(Name = "\"Строка с названием материала\"")] string line)
|
||||
{
|
||||
IDatabaseClient databaseClient = RhSolutionsAddIn.ServiceProvider.GetService<IDatabaseClient>();
|
||||
IEnumerable<Product> requestResult = ExcelAsyncUtil.Run("Database request", line, delegate
|
||||
|
||||
Sku.TryParse(line, out var skus);
|
||||
|
||||
if (ExcelAsyncUtil.Run("Database request", line, delegate
|
||||
{
|
||||
return databaseClient.GetProducts(line)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}) as IEnumerable<Product>;
|
||||
|
||||
Sku.TryParse(line, out var skus);
|
||||
|
||||
if (requestResult == null)
|
||||
}) is not IEnumerable<Product> requestResult)
|
||||
{
|
||||
if (skus.Count() > 0)
|
||||
if (skus.Any())
|
||||
{
|
||||
return $"{skus.First()} ...";
|
||||
}
|
||||
@ -29,12 +35,12 @@ public class RhSolutionsFunction
|
||||
|
||||
else
|
||||
{
|
||||
if (requestResult.Count() == 0 && skus.Count() == 0)
|
||||
if (!requestResult.Any() && !skus.Any())
|
||||
{
|
||||
return ExcelError.ExcelErrorNA;
|
||||
}
|
||||
|
||||
else if (requestResult.Count() == 0)
|
||||
else if (!requestResult.Any())
|
||||
{
|
||||
return $"{skus.First()}";
|
||||
}
|
||||
|
@ -1,59 +0,0 @@
|
||||
using RhSolutions.AddIn;
|
||||
|
||||
namespace RhSolutions.Controllers;
|
||||
|
||||
internal class CombineTool : ToolBase
|
||||
{
|
||||
private List<SourcePriceList> SourceFiles { get; set; }
|
||||
|
||||
public CombineTool()
|
||||
{
|
||||
var dialog = RhSolutionsAddIn.Excel.FileDialog[Microsoft.Office.Core.MsoFileDialogType.msoFileDialogFilePicker];
|
||||
dialog.AllowMultiSelect = true;
|
||||
dialog.Filters.Add("Файлы Excel", "*.xls; *.xlsx; *.xlsm");
|
||||
|
||||
if (dialog.Show() < 0)
|
||||
{
|
||||
List<string> files = new();
|
||||
|
||||
foreach (string file in dialog.SelectedItems)
|
||||
{
|
||||
files.Add(file);
|
||||
}
|
||||
|
||||
SourceFiles = SourcePriceList.GetSourceLists(files.ToArray());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
throw new Exception("Не выбраны файлы");
|
||||
}
|
||||
}
|
||||
|
||||
public override void FillTarget()
|
||||
{
|
||||
using (ProgressBar = new ProgressBar("Заполняю строки...", SourceFiles.Sum(file => file.PositionAmount.Count)))
|
||||
using (ResultBar = new ResultBar())
|
||||
{
|
||||
foreach (SourcePriceList source in SourceFiles)
|
||||
{
|
||||
TargetFile.Sheet.Columns[TargetFile.AmountCell.Column]
|
||||
.EntireColumn
|
||||
.Insert(XlInsertShiftDirection.xlShiftToRight, XlInsertFormatOrigin.xlFormatFromRightOrBelow);
|
||||
|
||||
Range newColumnHeader = TargetFile.Sheet.Cells[TargetFile.AmountCell.Row, TargetFile.AmountCell.Column - 1];
|
||||
newColumnHeader.Value2 = $"{source.Name}";
|
||||
newColumnHeader.WrapText = true;
|
||||
|
||||
foreach (var kvp in source.PositionAmount)
|
||||
{
|
||||
FillPositionAmountToColumns(kvp, TargetFile.AmountCell.Column - 1, TargetFile.AmountCell.Column);
|
||||
ProgressBar.Update();
|
||||
}
|
||||
}
|
||||
|
||||
FilterByAmount();
|
||||
ResultBar.Update();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
namespace RhSolutions.Controllers;
|
||||
|
||||
internal class ConvertTool : ToolBase
|
||||
{
|
||||
private SourcePriceList Current { get; set; }
|
||||
|
||||
public ConvertTool()
|
||||
{
|
||||
Current = new SourcePriceList(ExcelApp.ActiveWorkbook);
|
||||
}
|
||||
|
||||
public override void FillTarget()
|
||||
{
|
||||
using (ProgressBar = new ProgressBar("Заполняю строки...", Current.PositionAmount.Count))
|
||||
using (ResultBar = new ResultBar())
|
||||
{
|
||||
foreach (var kvp in Current.PositionAmount)
|
||||
{
|
||||
FillPositionAmountToColumns(kvp, TargetFile.AmountCell.Column);
|
||||
ProgressBar.Update();
|
||||
}
|
||||
|
||||
FilterByAmount();
|
||||
ResultBar.Update();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
using RhSolutions.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RhSolutions.Controllers;
|
||||
|
||||
internal class ExportTool : ToolBase
|
||||
{
|
||||
private Dictionary<Product, double> PositionAmount;
|
||||
private readonly Range Selection;
|
||||
|
||||
public ExportTool()
|
||||
{
|
||||
Selection = ExcelApp.Selection;
|
||||
GetSelected();
|
||||
|
||||
if (PositionAmount.Count == 0)
|
||||
{
|
||||
throw new Exception("В выделенном диапазоне не найдены позиции для экспорта");
|
||||
}
|
||||
}
|
||||
|
||||
public override void FillTarget()
|
||||
{
|
||||
using (ProgressBar = new ProgressBar("Заполняю строки...", PositionAmount.Count))
|
||||
using (ResultBar = new ResultBar())
|
||||
{
|
||||
foreach (var kvp in PositionAmount)
|
||||
{
|
||||
FillPositionAmountToColumns(kvp, TargetFile.AmountCell.Column);
|
||||
ProgressBar.Update();
|
||||
}
|
||||
|
||||
FilterByAmount();
|
||||
ResultBar.Update();
|
||||
}
|
||||
}
|
||||
|
||||
private void GetSelected()
|
||||
{
|
||||
object[,] cells = Selection.Value2;
|
||||
PositionAmount = new Dictionary<Product, double>();
|
||||
|
||||
int rowsCount = Selection.Rows.Count;
|
||||
|
||||
for (int row = 1; row <= rowsCount; row++)
|
||||
{
|
||||
if (cells[row, 1] == null || cells[row, 2] == null)
|
||||
continue;
|
||||
|
||||
string sku = null;
|
||||
double? amount = null;
|
||||
|
||||
for (int column = 1; column <= 2; column++)
|
||||
{
|
||||
object current = cells[row, column];
|
||||
|
||||
if (Sku.TryParse(current.ToString(), out var rauSku))
|
||||
{
|
||||
sku = rauSku.FirstOrDefault().ToString() ?? null;
|
||||
}
|
||||
|
||||
else if (current.GetType() == typeof(string)
|
||||
&& double.TryParse(current.ToString(), out _))
|
||||
{
|
||||
amount = double.Parse((string)current);
|
||||
}
|
||||
|
||||
else if (current.GetType() == typeof(double))
|
||||
{
|
||||
amount = (double)current;
|
||||
}
|
||||
}
|
||||
|
||||
if (sku == null || amount == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Product position = new Product
|
||||
{
|
||||
ProductSku = sku
|
||||
};
|
||||
|
||||
if (PositionAmount.ContainsKey(position))
|
||||
{
|
||||
PositionAmount[position] += amount.Value;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
PositionAmount.Add(position, amount.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,56 +0,0 @@
|
||||
using RhSolutions.AddIn;
|
||||
using RhSolutions.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RhSolutions.Controllers
|
||||
{
|
||||
internal class MergeTool : ToolBase
|
||||
{
|
||||
private List<SourcePriceList> SourceFiles { get; set; }
|
||||
|
||||
public MergeTool()
|
||||
{
|
||||
var dialog = RhSolutionsAddIn.Excel.FileDialog[Microsoft.Office.Core.MsoFileDialogType.msoFileDialogFilePicker];
|
||||
dialog.AllowMultiSelect = true;
|
||||
dialog.Filters.Add("Файлы Excel", "*.xls; *.xlsx; *.xlsm");
|
||||
|
||||
if ( dialog.Show() < 0)
|
||||
{
|
||||
List<string> files = new();
|
||||
|
||||
foreach (string file in dialog.SelectedItems)
|
||||
{
|
||||
files.Add(file);
|
||||
}
|
||||
|
||||
SourceFiles = SourcePriceList.GetSourceLists(files.ToArray());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
throw new Exception("Не выбраны файлы");
|
||||
}
|
||||
}
|
||||
|
||||
public override void FillTarget()
|
||||
{
|
||||
using (ProgressBar = new ProgressBar("Заполняю строки...", SourceFiles.Sum(x => x.PositionAmount.Count)))
|
||||
using (ResultBar = new ResultBar())
|
||||
{
|
||||
foreach (SourcePriceList source in SourceFiles)
|
||||
{
|
||||
foreach (var kvp in source.PositionAmount)
|
||||
{
|
||||
FillPositionAmountToColumns(kvp, TargetFile.AmountCell.Column);
|
||||
ProgressBar.Update();
|
||||
}
|
||||
}
|
||||
|
||||
FilterByAmount();
|
||||
ResultBar.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,17 @@
|
||||
using ExcelDna.Integration.CustomUI;
|
||||
using RhSolutions.AddIn;
|
||||
using RhSolutions.Tools;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
#if! NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace RhSolutions.Controllers;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
[ComVisible(true)]
|
||||
public class RibbonController : ExcelRibbon
|
||||
{
|
||||
@ -21,10 +27,7 @@ public class RibbonController : ExcelRibbon
|
||||
<group id='priceList' label='Прайс-лист'>
|
||||
<button id='export' getEnabled='GetExportEnabled' label='Экспорт в новый файл' size='normal' imageMso='PivotExportToExcel' onAction='OnToolPressed'/>
|
||||
<button id='convert' getEnabled='GetConvertEnabled' label='Актуализировать' size='normal' imageMso='FileUpdate' onAction='OnToolPressed'/>
|
||||
<menu id='conjoinMenu' label='Объединить' imageMso='Copy'>
|
||||
<button id='merge' label='Сложить' onAction='OnToolPressed'/>
|
||||
<button id='combine' label='По колонкам' onAction='OnToolPressed'/>
|
||||
</menu>
|
||||
<button id='merge' label='Объединить' size='normal' imageMso='Copy' onAction='OnToolPressed'/>
|
||||
</group>
|
||||
<group id='rausettings' getLabel='GetVersionLabel'>
|
||||
<button id='setPriceList' getLabel='GetPriceListPathLabel' size='large' imageMso='TableExcelSpreadsheetInsert' onAction='OnSetPricePressed'/>
|
||||
@ -42,22 +45,17 @@ public class RibbonController : ExcelRibbon
|
||||
|
||||
public static void RefreshControl(string id)
|
||||
{
|
||||
if (ribbonUi != null)
|
||||
{
|
||||
ribbonUi.InvalidateControl(id);
|
||||
}
|
||||
ribbonUi?.InvalidateControl(id);
|
||||
}
|
||||
|
||||
public void OnSetPricePressed(IRibbonControl control)
|
||||
{
|
||||
var dialog = RhSolutionsAddIn.Excel
|
||||
.FileDialog[Microsoft.Office.Core.MsoFileDialogType.msoFileDialogFilePicker];
|
||||
dialog.AllowMultiSelect = false;
|
||||
dialog.Filters.Add("Файлы Excel", "*.xls; *.xlsx; *.xlsm");
|
||||
IFileDialog dialog = RhSolutionsAddIn.ServiceProvider.GetService<IFileDialog>();
|
||||
string file = dialog.GetFile();
|
||||
|
||||
if (dialog.Show() < 0)
|
||||
if (!string.IsNullOrEmpty(file))
|
||||
{
|
||||
RhSolutionsAddIn.Configuration.SetPriceListPath(dialog.SelectedItems.Item(1));
|
||||
RhSolutionsAddIn.Configuration.SetPriceListPath(file);
|
||||
RhSolutionsAddIn.Configuration.SaveSettings();
|
||||
}
|
||||
}
|
||||
@ -71,11 +69,9 @@ public class RibbonController : ExcelRibbon
|
||||
"export" => new ExportTool(),
|
||||
"convert" => new ConvertTool(),
|
||||
"merge" => new MergeTool(),
|
||||
"combine" => new CombineTool(),
|
||||
_ => throw new Exception("Неизвестный инструмент"),
|
||||
};
|
||||
tool.OpenNewPrice();
|
||||
tool.FillTarget();
|
||||
tool.Execute();
|
||||
}
|
||||
|
||||
catch (Exception exception)
|
||||
@ -122,6 +118,6 @@ public class RibbonController : ExcelRibbon
|
||||
public string GetPriceListPathLabel(IRibbonControl control)
|
||||
{
|
||||
string name = RhSolutionsAddIn.Configuration.GetPriceListFileName();
|
||||
return string.IsNullOrEmpty(name) ? "Нет файла шаблона!" : name;
|
||||
return string.IsNullOrEmpty(name) ? "Шаблонный файл" : name;
|
||||
}
|
||||
}
|
||||
|
@ -1,183 +0,0 @@
|
||||
using RhSolutions.AddIn;
|
||||
|
||||
namespace RhSolutions.Controllers
|
||||
{
|
||||
internal abstract class ToolBase
|
||||
{
|
||||
protected Application ExcelApp = RhSolutionsAddIn.Excel;
|
||||
protected IAddInConfiguration Configuration = RhSolutionsAddIn.Configuration;
|
||||
protected TargetPriceList TargetFile { get; set; }
|
||||
protected ResultBar ResultBar { get; set; }
|
||||
protected ProgressBar ProgressBar { get; set; }
|
||||
public abstract void FillTarget();
|
||||
|
||||
public void OpenNewPrice()
|
||||
{
|
||||
if (ExcelApp.Workbooks
|
||||
.Cast<Workbook>()
|
||||
.FirstOrDefault(w => w.FullName == Configuration.GetPriceListPath()) != null)
|
||||
{
|
||||
throw new ArgumentException("Шаблонный файл редактируется в другом месте");
|
||||
}
|
||||
|
||||
Workbook wb = ExcelApp.Workbooks.Open(Configuration.GetPriceListPath(), null, true);
|
||||
|
||||
try
|
||||
{
|
||||
TargetFile = new TargetPriceList(wb);
|
||||
}
|
||||
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (wb != null)
|
||||
{
|
||||
wb.Close();
|
||||
}
|
||||
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
protected void FillPositionAmountToColumns(KeyValuePair<Product, double> positionAmount, params int[] columns)
|
||||
{
|
||||
Range worksheetCells = TargetFile.Sheet.Cells;
|
||||
Range skuColumn = TargetFile.SkuCell.EntireColumn;
|
||||
|
||||
int? row = GetPositionRow(skuColumn, positionAmount.Key.ProductSku, positionAmount.Key.ProductLine);
|
||||
|
||||
if (row != null)
|
||||
{
|
||||
foreach (int column in columns)
|
||||
{
|
||||
Range cell = worksheetCells[row, column];
|
||||
cell.AddValue(positionAmount.Value);
|
||||
}
|
||||
|
||||
ResultBar.IncrementSuccess();
|
||||
return;
|
||||
}
|
||||
|
||||
if (TargetFile.OldSkuCell != null)
|
||||
{
|
||||
row = GetPositionRow(TargetFile.OldSkuCell.EntireColumn, positionAmount.Key.ProductSku, positionAmount.Key.ProductLine);
|
||||
|
||||
if (row != null)
|
||||
{
|
||||
foreach (int column in columns)
|
||||
{
|
||||
Range cell = worksheetCells[row, column];
|
||||
cell.AddValue(positionAmount.Value);
|
||||
}
|
||||
|
||||
ResultBar.IncrementReplaced();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string sku = positionAmount.Key.ProductSku.Substring(1, 6);
|
||||
row = GetPositionRow(skuColumn, sku, positionAmount.Key.ProductLine);
|
||||
|
||||
if (row != null)
|
||||
{
|
||||
foreach (int column in columns)
|
||||
{
|
||||
Range cell = worksheetCells[row, column];
|
||||
cell.AddValue(positionAmount.Value);
|
||||
}
|
||||
|
||||
ResultBar.IncrementReplaced();
|
||||
return;
|
||||
}
|
||||
|
||||
FillMissing(positionAmount, columns);
|
||||
ResultBar.IncrementNotFound();
|
||||
}
|
||||
|
||||
protected void FillMissing(KeyValuePair<Product, double> positionAmount, params int[] columns)
|
||||
{
|
||||
Range worksheetCells = TargetFile.Sheet.Cells;
|
||||
Range worksheetRows = TargetFile.Sheet.Rows;
|
||||
int skuColumn = TargetFile.SkuCell.Column;
|
||||
int groupColumn = TargetFile.GroupCell.Column;
|
||||
int nameColumn = TargetFile.NameCell.Column;
|
||||
|
||||
int row = worksheetCells[worksheetRows.Count, skuColumn]
|
||||
.End[XlDirection.xlUp]
|
||||
.Row + 1;
|
||||
|
||||
worksheetRows[row]
|
||||
.EntireRow
|
||||
.Insert(XlInsertShiftDirection.xlShiftDown, XlInsertFormatOrigin.xlFormatFromLeftOrAbove);
|
||||
|
||||
Range previous = worksheetRows[row - 1];
|
||||
Range current = worksheetRows[row];
|
||||
|
||||
previous.Copy(current);
|
||||
current.ClearContents();
|
||||
|
||||
worksheetCells[row, groupColumn].Value2 = positionAmount.Key.ProductLine;
|
||||
worksheetCells[row, nameColumn].Value2 = positionAmount.Key.Name;
|
||||
|
||||
if (TargetFile.OldSkuCell != null)
|
||||
{
|
||||
worksheetCells[row, skuColumn].Value2 = "Не найден";
|
||||
worksheetCells[row, TargetFile.OldSkuCell.Column].Value2 = positionAmount.Key.ProductSku;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
worksheetCells[row, skuColumn].Value2 = positionAmount.Key.ProductSku;
|
||||
}
|
||||
|
||||
foreach (int column in columns)
|
||||
{
|
||||
Range cell = worksheetCells[row, column];
|
||||
cell.AddValue(positionAmount.Value);
|
||||
}
|
||||
}
|
||||
|
||||
protected int? GetPositionRow(Range range, string sku, string group)
|
||||
{
|
||||
Range found = range.Find(sku);
|
||||
string foundGroupValue;
|
||||
|
||||
if (found == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int firstFoundRow = found.Row;
|
||||
|
||||
if (string.IsNullOrEmpty(group))
|
||||
{
|
||||
return found.Row;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
foundGroupValue = TargetFile.Sheet.Cells[found.Row, TargetFile.GroupCell.Column].Value2.ToString();
|
||||
|
||||
if (group.Equals(foundGroupValue))
|
||||
{
|
||||
return found.Row;
|
||||
}
|
||||
|
||||
found = range.FindNext(found);
|
||||
|
||||
if (found.Row == firstFoundRow)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void FilterByAmount()
|
||||
{
|
||||
AutoFilter filter = TargetFile.Sheet.AutoFilter;
|
||||
int startColumn = filter.Range.Column;
|
||||
|
||||
filter.Range.AutoFilter(TargetFile.AmountCell.Column - startColumn + 1, "<>0", XlAutoFilterOperator.xlAnd, "<>");
|
||||
TargetFile.Sheet.Range["A1"].Activate();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
using Microsoft.Office.Interop.Excel;
|
||||
|
||||
namespace RhSolutions.Models
|
||||
{
|
||||
internal abstract class PriceListBase
|
||||
{
|
||||
public Range AmountCell { get; protected set; }
|
||||
public Range SkuCell { get; protected set; }
|
||||
public Range GroupCell { get; protected set; }
|
||||
public Range NameCell { get; protected set; }
|
||||
|
||||
public Worksheet Sheet { get; protected set; }
|
||||
public string Name { get; protected set; }
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
namespace RhSolutions.Models
|
||||
{
|
||||
internal class ProgressBar : StatusbarBase
|
||||
{
|
||||
private double CurrentProgress { get; set; }
|
||||
private readonly double TaskWeight;
|
||||
private readonly string Message;
|
||||
|
||||
public ProgressBar(string message, int weight)
|
||||
{
|
||||
Message = message;
|
||||
TaskWeight = weight;
|
||||
CurrentProgress = 0;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
double percent = ++CurrentProgress / TaskWeight * 100;
|
||||
Excel.StatusBar = $"{Message} Выполнено {percent:#.#} %";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace RhSolutions.Models
|
||||
{
|
||||
internal class ResultBar : StatusbarBase
|
||||
{
|
||||
private int Success { get; set; }
|
||||
private int Replaced { get; set; }
|
||||
private int NotFound { get; set; }
|
||||
|
||||
public ResultBar()
|
||||
{
|
||||
Success = 0;
|
||||
Replaced = 0;
|
||||
NotFound = 0;
|
||||
}
|
||||
|
||||
public void IncrementSuccess() => Success++;
|
||||
public void IncrementReplaced() => Replaced++;
|
||||
public void IncrementNotFound() => NotFound++;
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (Success > 0)
|
||||
{
|
||||
sb.Append($"Успешно экспортировано {Success} артикулов. ");
|
||||
}
|
||||
|
||||
if (Replaced > 0)
|
||||
{
|
||||
sb.Append($"Заменено {Replaced} артикулов. ");
|
||||
}
|
||||
|
||||
if (NotFound > 0)
|
||||
{
|
||||
sb.Append($"Не найдено {NotFound} артикулов.");
|
||||
}
|
||||
|
||||
Excel.StatusBar = sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
using RhSolutions.AddIn;
|
||||
using System.IO;
|
||||
|
||||
namespace RhSolutions.Models;
|
||||
|
||||
internal class SourcePriceList : PriceListBase
|
||||
{
|
||||
public Dictionary<Product, double> PositionAmount { get; private set; }
|
||||
|
||||
public SourcePriceList(Workbook workbook)
|
||||
{
|
||||
if (workbook == null)
|
||||
{
|
||||
throw new ArgumentException($"Нет рабочего файла");
|
||||
}
|
||||
|
||||
Sheet = workbook.ActiveSheet;
|
||||
Name = Path.GetFileNameWithoutExtension(workbook.FullName);
|
||||
|
||||
var pricelistParameters = RhSolutionsAddIn.Configuration.GetPriceListParameters();
|
||||
|
||||
Range[] cells = new[]
|
||||
{
|
||||
AmountCell = Sheet.Cells.Find(pricelistParameters["Amount"]),
|
||||
SkuCell = Sheet.Cells.Find(pricelistParameters["Sku"]),
|
||||
GroupCell = Sheet.Cells.Find(pricelistParameters["Group"]),
|
||||
NameCell = Sheet.Cells.Find(pricelistParameters["Name"])
|
||||
};
|
||||
|
||||
if (cells.Any(x => x == null))
|
||||
{
|
||||
throw new ArgumentException($"Файл {Name} не распознан");
|
||||
}
|
||||
|
||||
CreatePositionsDict();
|
||||
}
|
||||
|
||||
public static List<SourcePriceList> GetSourceLists(string[] files)
|
||||
{
|
||||
var ExcelApp = (Application)ExcelDnaUtil.Application;
|
||||
ProgressBar bar = new ProgressBar("Открываю исходные файлы...", files.Length);
|
||||
|
||||
List<SourcePriceList> sourceFiles = new List<SourcePriceList>();
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
ExcelApp.ScreenUpdating = false;
|
||||
Workbook wb = ExcelApp.Workbooks.Open(file);
|
||||
try
|
||||
{
|
||||
SourcePriceList priceList = new SourcePriceList(wb);
|
||||
sourceFiles.Add(priceList);
|
||||
wb.Close();
|
||||
bar.Update();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Windows.Forms.MessageBox.Show
|
||||
(ex.Message,
|
||||
"Ошибка открытия исходного прайс-листа",
|
||||
System.Windows.Forms.MessageBoxButtons.OK,
|
||||
System.Windows.Forms.MessageBoxIcon.Information);
|
||||
wb.Close();
|
||||
bar.Update();
|
||||
}
|
||||
ExcelApp.ScreenUpdating = true;
|
||||
}
|
||||
|
||||
return sourceFiles;
|
||||
}
|
||||
|
||||
private void CreatePositionsDict()
|
||||
{
|
||||
PositionAmount = new Dictionary<Product, double>();
|
||||
|
||||
for (int row = AmountCell.Row + 1; row <= Sheet.Cells[Sheet.Rows.Count, AmountCell.Column].End[XlDirection.xlUp].Row; row++)
|
||||
{
|
||||
double? amount = Sheet.Cells[row, AmountCell.Column].Value2 as double?;
|
||||
|
||||
if (amount != null && amount.Value != 0)
|
||||
{
|
||||
object group = Sheet.Cells[row, GroupCell.Column].Value2;
|
||||
object name = Sheet.Cells[row, NameCell.Column].Value2;
|
||||
object sku = Sheet.Cells[row, SkuCell.Column].Value2;
|
||||
|
||||
if (group == null || name == null || sku == null)
|
||||
continue;
|
||||
|
||||
if (!Sku.TryParse(sku.ToString(), out _))
|
||||
continue;
|
||||
|
||||
Product p = new Product
|
||||
{
|
||||
ProductSku = sku.ToString(),
|
||||
ProductLine = group.ToString(),
|
||||
Name = name.ToString()
|
||||
};
|
||||
|
||||
if (PositionAmount.ContainsKey(p))
|
||||
{
|
||||
PositionAmount[p] += amount.Value;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
PositionAmount.Add(p, amount.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +0,0 @@
|
||||
using Microsoft.Office.Interop.Excel;
|
||||
using RhSolutions.AddIn;
|
||||
using System;
|
||||
|
||||
namespace RhSolutions.Models
|
||||
{
|
||||
internal abstract class StatusbarBase : IDisposable
|
||||
{
|
||||
protected Application Excel = RhSolutionsAddIn.Excel;
|
||||
|
||||
public abstract void Update();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Excel.OnTime(DateTime.Now + new TimeSpan(0, 0, 5), "_ResetStatusBar");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
using RhSolutions.AddIn;
|
||||
using System.IO;
|
||||
|
||||
namespace RhSolutions.Models;
|
||||
|
||||
internal class TargetPriceList : PriceListBase
|
||||
{
|
||||
public Range OldSkuCell { get; private set; }
|
||||
|
||||
public TargetPriceList(Workbook workbook)
|
||||
{
|
||||
if (workbook == null)
|
||||
{
|
||||
throw new ArgumentException("Невозможно открыть книгу шаблонного файла. " +
|
||||
"Возможно открыт файл с именем, совпадающим с именем шаблонного файла.");
|
||||
}
|
||||
|
||||
Sheet = workbook.ActiveSheet;
|
||||
Name = Path.GetFileNameWithoutExtension(workbook.FullName);
|
||||
|
||||
var pricelistParameters = RhSolutionsAddIn.Configuration.GetPriceListParameters();
|
||||
|
||||
Range[] cells = new[]
|
||||
{
|
||||
AmountCell = Sheet.Cells.Find(pricelistParameters["Amount"]),
|
||||
SkuCell = Sheet.Cells.Find(pricelistParameters["Sku"]),
|
||||
GroupCell = Sheet.Cells.Find(pricelistParameters["Group"]),
|
||||
NameCell = Sheet.Cells.Find(pricelistParameters["Name"])
|
||||
};
|
||||
|
||||
OldSkuCell = Sheet.Cells.Find(pricelistParameters["OldSku"]);
|
||||
|
||||
if (cells.Any(x => x == null))
|
||||
{
|
||||
throw new ArgumentException($"Шаблон {Name} не является прайс-листом");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,4 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="RhSolutions.Sku" Version="0.1.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RhSolutions.ExcelExtensions\RhSolutions.ExcelExtensions.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
45
RhSolutions.AddIn/Services/ExcelFileDialog.cs
Normal file
45
RhSolutions.AddIn/Services/ExcelFileDialog.cs
Normal file
@ -0,0 +1,45 @@
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
public class ExcelFileDialog : IFileDialog
|
||||
{
|
||||
private Application _application;
|
||||
|
||||
public ExcelFileDialog(Application application)
|
||||
{
|
||||
_application = application;
|
||||
}
|
||||
|
||||
public string GetFile()
|
||||
{
|
||||
var dialog = _application.FileDialog[Microsoft.Office.Core.MsoFileDialogType.msoFileDialogFilePicker];
|
||||
dialog.AllowMultiSelect = false;
|
||||
dialog.Filters.Add("Файлы Excel", "*.xls; *.xlsx; *.xlsm");
|
||||
|
||||
if (dialog.Show() == -1)
|
||||
{
|
||||
return dialog.SelectedItems.Item(1);
|
||||
}
|
||||
else return string.Empty;
|
||||
}
|
||||
|
||||
public string[] GetFiles()
|
||||
{
|
||||
var dialog = _application.FileDialog[Microsoft.Office.Core.MsoFileDialogType.msoFileDialogFilePicker];
|
||||
dialog.AllowMultiSelect = true;
|
||||
dialog.Filters.Add("Файлы Excel", "*.xls; *.xlsx; *.xlsm");
|
||||
|
||||
if (dialog.Show() == -1)
|
||||
{
|
||||
List<string> files = new();
|
||||
|
||||
foreach (string file in dialog.SelectedItems)
|
||||
{
|
||||
files.Add(file);
|
||||
}
|
||||
|
||||
return files.ToArray();
|
||||
}
|
||||
|
||||
else return Array.Empty<string>();
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ public interface IAddInConfiguration
|
||||
{
|
||||
public string GetPriceListPath();
|
||||
public string GetPriceListFileName();
|
||||
public Dictionary<string, string> GetPriceListParameters();
|
||||
public Dictionary<string, string> GetPriceListHeaders();
|
||||
public event SettingChangingEventHandler OnSettingsChange;
|
||||
public void SetPriceListPath(string value);
|
||||
public void SaveSettings();
|
||||
|
8
RhSolutions.AddIn/Services/IExcelReader.cs
Normal file
8
RhSolutions.AddIn/Services/IExcelReader.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
public interface IExcelReader
|
||||
{
|
||||
public Dictionary<Product, double> ReadProducts(Range range);
|
||||
public List<(string, Dictionary<Product, double>)> ReadProducts(IEnumerable<Worksheet> worksheets);
|
||||
public List<(string, Dictionary<Product, double>)> ReadProducts(string[] files);
|
||||
}
|
7
RhSolutions.AddIn/Services/IExcelWriter.cs
Normal file
7
RhSolutions.AddIn/Services/IExcelWriter.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
public interface IExcelWriter
|
||||
{
|
||||
public void WriteProducts(IEnumerable<(string, Dictionary<Product, double>)> products);
|
||||
public void WriteProducts(Dictionary<Product, double> products);
|
||||
}
|
7
RhSolutions.AddIn/Services/IFileDialog.cs
Normal file
7
RhSolutions.AddIn/Services/IFileDialog.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
public interface IFileDialog
|
||||
{
|
||||
public string[] GetFiles();
|
||||
public string GetFile();
|
||||
}
|
@ -3,18 +3,18 @@ using System.IO;
|
||||
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
public class AddInConfiguration : ApplicationSettingsBase, IAddInConfiguration
|
||||
public class RhAddInConfiguration : ApplicationSettingsBase, IAddInConfiguration
|
||||
{
|
||||
private Dictionary<string, string> _priceListParameters;
|
||||
private readonly Dictionary<string, string> _priceListHeaders;
|
||||
|
||||
public AddInConfiguration()
|
||||
public RhAddInConfiguration()
|
||||
{
|
||||
_priceListParameters = new Dictionary<string, string>()
|
||||
_priceListHeaders = new Dictionary<string, string>()
|
||||
{
|
||||
["Amount"] = AmountHeader,
|
||||
["OldSku"] = OldSkuHeader,
|
||||
["Sku"] = SkuHeader,
|
||||
["Group"] = GroupHeader,
|
||||
["ProductLine"] = ProductLineHeader,
|
||||
["Name"] = NameHeader
|
||||
};
|
||||
}
|
||||
@ -52,11 +52,11 @@ public class AddInConfiguration : ApplicationSettingsBase, IAddInConfiguration
|
||||
|
||||
[UserScopedSetting]
|
||||
[DefaultSettingValue("Программа")]
|
||||
public string GroupHeader
|
||||
public string ProductLineHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)this[nameof(GroupHeader)];
|
||||
return (string)this[nameof(ProductLineHeader)];
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,5 +99,5 @@ public class AddInConfiguration : ApplicationSettingsBase, IAddInConfiguration
|
||||
public string GetPriceListPath() => PriceListPath;
|
||||
public void SetPriceListPath(string value) => PriceListPath = value;
|
||||
public void SaveSettings() => base.Save();
|
||||
public Dictionary<string, string> GetPriceListParameters() => _priceListParameters;
|
||||
public Dictionary<string, string> GetPriceListHeaders() => _priceListHeaders;
|
||||
}
|
169
RhSolutions.AddIn/Services/RhExcelReader.cs
Normal file
169
RhSolutions.AddIn/Services/RhExcelReader.cs
Normal file
@ -0,0 +1,169 @@
|
||||
using System.IO;
|
||||
using RhSolutions.Tools;
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
using RhSolutions.Tools;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
public class RhExcelReader : IExcelReader, IDisposable
|
||||
{
|
||||
private ProgressBar _progressBar;
|
||||
private readonly Dictionary<string, string> headers;
|
||||
private readonly Application _application;
|
||||
|
||||
public RhExcelReader(Application application, IAddInConfiguration configuration)
|
||||
{
|
||||
_application = application;
|
||||
headers = configuration.GetPriceListHeaders();
|
||||
}
|
||||
|
||||
public Dictionary<Product, double> ReadProducts(Range range)
|
||||
{
|
||||
object[,] cells = range.Value2;
|
||||
Dictionary<Product, double> readResult = new();
|
||||
|
||||
for (int row = 1; row <= range.Rows.Count; row++)
|
||||
{
|
||||
if (cells[row, 1] == null || cells[row, 2] == null)
|
||||
continue;
|
||||
|
||||
string currentSku = null;
|
||||
double? currentAmount = null;
|
||||
|
||||
for (int column = 1; column <= 2; column++)
|
||||
{
|
||||
object currentCell = cells[row, column];
|
||||
|
||||
if (Sku.TryParse(currentCell.ToString(), out var validSku))
|
||||
{
|
||||
currentSku = validSku.FirstOrDefault().ToString() ?? null;
|
||||
}
|
||||
|
||||
else if (currentCell.GetType() == typeof(string)
|
||||
&& double.TryParse(currentCell.ToString(), out _))
|
||||
{
|
||||
currentAmount = double.Parse((string)currentCell);
|
||||
}
|
||||
|
||||
else if (currentCell.GetType() == typeof(double))
|
||||
{
|
||||
currentAmount = (double)currentCell;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentSku == null || currentAmount == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Product product = new() { ProductSku = currentSku };
|
||||
|
||||
if (readResult.ContainsKey(product))
|
||||
{
|
||||
readResult[product] += currentAmount.Value;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
readResult.Add(product, currentAmount.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return readResult;
|
||||
}
|
||||
|
||||
public List<(string, Dictionary<Product, double>)>
|
||||
ReadProducts(IEnumerable<Worksheet> worksheets)
|
||||
{
|
||||
List<(string, Dictionary<Product, double>)> result = new();
|
||||
foreach (Worksheet worksheet in worksheets)
|
||||
{
|
||||
if (!worksheet.IsRehauSource())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string wbName = Path.GetFileNameWithoutExtension(
|
||||
worksheet.Parent.Name);
|
||||
|
||||
Range AmountCell = worksheet.Cells.Find(headers["Amount"]),
|
||||
SkuCell = worksheet.Cells.Find(headers["Sku"]),
|
||||
ProductLineCelll = worksheet.Cells.Find(headers["ProductLine"]),
|
||||
NameCell = worksheet.Cells.Find(headers["Name"]);
|
||||
var lastRowIndex = worksheet.Cells[worksheet.Rows.Count, AmountCell.Column]
|
||||
.End[XlDirection.xlUp].Row;
|
||||
|
||||
Dictionary<Product, double> readResult = new();
|
||||
|
||||
for (int row = AmountCell.Row + 1; row <= lastRowIndex; row++)
|
||||
{
|
||||
double? amount = worksheet.Cells[row, AmountCell.Column].Value2 as double?;
|
||||
|
||||
if (amount != null && amount.Value != 0)
|
||||
{
|
||||
object programLine = worksheet.Cells[row, ProductLineCelll.Column].Value2;
|
||||
object name = worksheet.Cells[row, NameCell.Column].Value2;
|
||||
object sku = worksheet.Cells[row, SkuCell.Column].Value2;
|
||||
|
||||
if (programLine == null || name == null || sku == null)
|
||||
continue;
|
||||
|
||||
if (!Sku.TryParse(sku.ToString(), out _))
|
||||
continue;
|
||||
|
||||
Product p = new()
|
||||
{
|
||||
ProductSku = sku.ToString(),
|
||||
ProductLine = programLine.ToString(),
|
||||
Name = name.ToString()
|
||||
};
|
||||
|
||||
if (readResult.ContainsKey(p))
|
||||
{
|
||||
readResult[p] += amount.Value;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
readResult.Add(p, amount.Value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
result.Add((wbName, readResult));
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
256
RhSolutions.AddIn/Services/RhExcelWriter.cs
Normal file
256
RhSolutions.AddIn/Services/RhExcelWriter.cs
Normal file
@ -0,0 +1,256 @@
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
using RhSolutions.Tools;
|
||||
#endif
|
||||
|
||||
using RhSolutions.Tools;
|
||||
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
public class RhExcelWriter : IExcelWriter, IDisposable
|
||||
{
|
||||
private readonly Application _application;
|
||||
private Worksheet _worksheet;
|
||||
private readonly ResultBar _resultBar;
|
||||
private readonly Dictionary<string, string> _headers;
|
||||
private readonly string _pricelistPath;
|
||||
private ProgressBar _progressBar;
|
||||
|
||||
private Range _amountCell,
|
||||
_skuCell,
|
||||
_programLineCell,
|
||||
_nameCell,
|
||||
_oldSkuCell;
|
||||
|
||||
public RhExcelWriter(Application application, IAddInConfiguration configuration)
|
||||
{
|
||||
_application = application;
|
||||
_pricelistPath = configuration.GetPriceListPath();
|
||||
_resultBar = new();
|
||||
_headers = configuration.GetPriceListHeaders();
|
||||
}
|
||||
|
||||
public void WriteProducts(Dictionary<Product, double> products)
|
||||
{
|
||||
WriteProducts(new[] { (string.Empty, products) });
|
||||
}
|
||||
|
||||
public void WriteProducts(IEnumerable<(string, Dictionary<Product, double>)> products)
|
||||
{
|
||||
_worksheet = OpenNewPrice();
|
||||
|
||||
if (!_worksheet.IsRehauSource())
|
||||
{
|
||||
_application.ActiveWorkbook.Close();
|
||||
throw new ArgumentException(
|
||||
$"Целевой файл {_application.ActiveWorkbook.Name} не является прайс-листом.");
|
||||
}
|
||||
|
||||
_amountCell = _worksheet.Cells.Find(_headers["Amount"]);
|
||||
_skuCell = _worksheet.Cells.Find(_headers["Sku"]);
|
||||
_programLineCell = _worksheet.Cells.Find(_headers["ProductLine"]);
|
||||
_nameCell = _worksheet.Cells.Find(_headers["Name"]);
|
||||
_oldSkuCell = _worksheet.Cells.Find(_headers["OldSku"]);
|
||||
|
||||
_progressBar = new("Заполняю строки...", products
|
||||
.Select(p => p.Item2)
|
||||
.Sum(set => set.Count));
|
||||
|
||||
if (products.Count() == 1)
|
||||
{
|
||||
foreach (var kvp in products.First().Item2)
|
||||
{
|
||||
FillPositionAmountToColumns(kvp, _amountCell.Column);
|
||||
_progressBar.Update();
|
||||
}
|
||||
FilterByAmount();
|
||||
_resultBar.Update();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
foreach (var product in products)
|
||||
{
|
||||
_worksheet.Columns[_amountCell.Column]
|
||||
.EntireColumn
|
||||
.Insert(XlInsertShiftDirection.xlShiftToRight, XlInsertFormatOrigin.xlFormatFromRightOrBelow);
|
||||
|
||||
Range newColumnHeader = _worksheet.Cells[_amountCell.Row, _amountCell.Column - 1];
|
||||
newColumnHeader.Value2 = $"{product.Item1}";
|
||||
newColumnHeader.WrapText = true;
|
||||
|
||||
foreach (var kvp in product.Item2)
|
||||
{
|
||||
FillPositionAmountToColumns(kvp, _amountCell.Column - 1, _amountCell.Column);
|
||||
_progressBar.Update();
|
||||
}
|
||||
}
|
||||
|
||||
FilterByAmount();
|
||||
_resultBar.Update();
|
||||
}
|
||||
}
|
||||
|
||||
private Worksheet OpenNewPrice()
|
||||
{
|
||||
if (_application.Workbooks
|
||||
.Cast<Workbook>()
|
||||
.FirstOrDefault(w => w.FullName == _pricelistPath) != null)
|
||||
{
|
||||
throw new ArgumentException("Шаблонный файл редактируется в другом месте");
|
||||
}
|
||||
|
||||
return _application.Workbooks.Open(_pricelistPath, null, true).ActiveSheet;
|
||||
}
|
||||
|
||||
private void FillPositionAmountToColumns(KeyValuePair<Product, double> positionAmount, params int[] columns)
|
||||
{
|
||||
Range worksheetCells = _worksheet.Cells;
|
||||
Range skuColumn = _skuCell.EntireColumn;
|
||||
|
||||
int? row = GetPositionRow(skuColumn, positionAmount.Key.ProductSku, positionAmount.Key.ProductLine);
|
||||
|
||||
if (row != null)
|
||||
{
|
||||
foreach (int column in columns)
|
||||
{
|
||||
Range cell = worksheetCells[row, column];
|
||||
cell.AddValue(positionAmount.Value);
|
||||
}
|
||||
|
||||
_resultBar.IncrementSuccess();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_oldSkuCell != null)
|
||||
{
|
||||
row = GetPositionRow(_oldSkuCell.EntireColumn, positionAmount.Key.ProductSku, positionAmount.Key.ProductLine);
|
||||
|
||||
if (row != null)
|
||||
{
|
||||
foreach (int column in columns)
|
||||
{
|
||||
Range cell = worksheetCells[row, column];
|
||||
cell.AddValue(positionAmount.Value);
|
||||
}
|
||||
|
||||
_resultBar.IncrementReplaced();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string sku = positionAmount.Key.ProductSku.Substring(1, 6);
|
||||
row = GetPositionRow(skuColumn, sku, positionAmount.Key.ProductLine);
|
||||
|
||||
if (row != null)
|
||||
{
|
||||
foreach (int column in columns)
|
||||
{
|
||||
Range cell = worksheetCells[row, column];
|
||||
cell.AddValue(positionAmount.Value);
|
||||
}
|
||||
|
||||
_resultBar.IncrementReplaced();
|
||||
return;
|
||||
}
|
||||
|
||||
FillMissing(positionAmount, columns);
|
||||
_resultBar.IncrementNotFound();
|
||||
}
|
||||
|
||||
private void FillMissing(KeyValuePair<Product, double> positionAmount, params int[] columns)
|
||||
{
|
||||
Range worksheetCells = _worksheet.Cells;
|
||||
Range worksheetRows = _worksheet.Rows;
|
||||
int skuColumn = _skuCell.Column;
|
||||
int groupColumn = _programLineCell.Column;
|
||||
int nameColumn = _nameCell.Column;
|
||||
|
||||
int row = worksheetCells[worksheetRows.Count, skuColumn]
|
||||
.End[XlDirection.xlUp]
|
||||
.Row + 1;
|
||||
|
||||
worksheetRows[row]
|
||||
.EntireRow
|
||||
.Insert(XlInsertShiftDirection.xlShiftDown, XlInsertFormatOrigin.xlFormatFromLeftOrAbove);
|
||||
|
||||
Range previous = worksheetRows[row - 1];
|
||||
Range current = worksheetRows[row];
|
||||
|
||||
previous.Copy(current);
|
||||
current.ClearContents();
|
||||
|
||||
worksheetCells[row, groupColumn].Value2 = positionAmount.Key.ProductLine;
|
||||
worksheetCells[row, nameColumn].Value2 = positionAmount.Key.Name;
|
||||
|
||||
if (_oldSkuCell != null)
|
||||
{
|
||||
worksheetCells[row, skuColumn].Value2 = "Не найден";
|
||||
worksheetCells[row, _oldSkuCell.Column].Value2 = positionAmount.Key.ProductSku;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
worksheetCells[row, skuColumn].Value2 = positionAmount.Key.ProductSku;
|
||||
}
|
||||
|
||||
foreach (int column in columns)
|
||||
{
|
||||
Range cell = worksheetCells[row, column];
|
||||
cell.AddValue(positionAmount.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private int? GetPositionRow(Range range, string sku, string group)
|
||||
{
|
||||
Range found = range.Find(sku);
|
||||
string foundGroupValue;
|
||||
|
||||
if (found == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int firstFoundRow = found.Row;
|
||||
|
||||
if (string.IsNullOrEmpty(group))
|
||||
{
|
||||
return found.Row;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
foundGroupValue = _worksheet.Cells[found.Row, _programLineCell.Column].Value2.ToString();
|
||||
|
||||
if (group.Equals(foundGroupValue))
|
||||
{
|
||||
return found.Row;
|
||||
}
|
||||
|
||||
found = range.FindNext(found);
|
||||
|
||||
if (found.Row == firstFoundRow)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FilterByAmount()
|
||||
{
|
||||
AutoFilter filter = _worksheet.AutoFilter;
|
||||
int startColumn = filter.Range.Column;
|
||||
|
||||
filter.Range.AutoFilter(_amountCell.Column - startColumn + 1, "<>0", XlAutoFilterOperator.xlAnd, "<>");
|
||||
_worksheet.Range["A1"].Activate();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_progressBar.Dispose();
|
||||
_resultBar.Dispose();
|
||||
}
|
||||
}
|
@ -1,23 +1,28 @@
|
||||
using RhSolutions.AddIn;
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
public static class WorksheetExtensions
|
||||
{
|
||||
public static bool IsRehauSource(this Worksheet worksheet)
|
||||
{
|
||||
Range amountCell;
|
||||
Range skuCell;
|
||||
Range groupCell;
|
||||
Range programLineCell;
|
||||
Range nameCell;
|
||||
|
||||
var pricelistParameters = RhSolutionsAddIn.Configuration.GetPriceListParameters();
|
||||
var pricelistParameters = RhSolutionsAddIn.Configuration.GetPriceListHeaders();
|
||||
|
||||
Range[] cells = new[]
|
||||
{
|
||||
amountCell = worksheet.Cells.Find(pricelistParameters["Amount"]),
|
||||
skuCell = worksheet.Cells.Find(pricelistParameters["Sku"]),
|
||||
groupCell = worksheet.Cells.Find(pricelistParameters["Group"]),
|
||||
programLineCell = worksheet.Cells.Find(pricelistParameters["ProductLine"]),
|
||||
nameCell = worksheet.Cells.Find(pricelistParameters["Name"])
|
||||
};
|
||||
|
20
RhSolutions.AddIn/Tools/ConvertTool.cs
Normal file
20
RhSolutions.AddIn/Tools/ConvertTool.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using RhSolutions.AddIn;
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.Tools;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
internal class ConvertTool : ToolBase
|
||||
{
|
||||
public override void Execute()
|
||||
{
|
||||
Application app = RhSolutionsAddIn.Excel.Application;
|
||||
Worksheet worksheet = app.ActiveWorkbook.ActiveSheet;
|
||||
var products = _reader.ReadProducts(new[] { worksheet });
|
||||
_writer.WriteProducts(products);
|
||||
}
|
||||
}
|
@ -2,9 +2,15 @@
|
||||
using RhSolutions.AddIn;
|
||||
using RhSolutions.Controllers;
|
||||
using System.Configuration;
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.Services
|
||||
namespace RhSolutions.Tools
|
||||
{
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
internal static class EventsUtil
|
||||
{
|
||||
public static void Initialize()
|
20
RhSolutions.AddIn/Tools/ExportTool.cs
Normal file
20
RhSolutions.AddIn/Tools/ExportTool.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using RhSolutions.AddIn;
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.Tools;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
internal class ExportTool : ToolBase
|
||||
{
|
||||
public override void Execute()
|
||||
{
|
||||
Application app = RhSolutionsAddIn.Excel.Application;
|
||||
var products = _reader.ReadProducts(app.Selection);
|
||||
_writer.WriteProducts(products);
|
||||
}
|
||||
}
|
||||
|
23
RhSolutions.AddIn/Tools/MergeTool.cs
Normal file
23
RhSolutions.AddIn/Tools/MergeTool.cs
Normal file
@ -0,0 +1,23 @@
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
using RhSolutions;
|
||||
using RhSolutions.Models;
|
||||
using RhSolutions.Tools;
|
||||
#endif
|
||||
|
||||
|
||||
namespace RhSolutions.Tools;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
internal class MergeTool : ToolBase
|
||||
{
|
||||
public override void Execute()
|
||||
{
|
||||
IFileDialog dialog = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IFileDialog>();
|
||||
string[] files = dialog.GetFiles();
|
||||
var products = _reader.ReadProducts(files);
|
||||
_writer.WriteProducts(products);
|
||||
}
|
||||
}
|
28
RhSolutions.AddIn/Tools/ProgressBar.cs
Normal file
28
RhSolutions.AddIn/Tools/ProgressBar.cs
Normal file
@ -0,0 +1,28 @@
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.Tools;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
internal class ProgressBar : StatusbarBase
|
||||
{
|
||||
private double CurrentProgress { get; set; }
|
||||
private readonly double TaskWeight;
|
||||
private readonly string Message;
|
||||
|
||||
public ProgressBar(string message, int weight)
|
||||
{
|
||||
Message = message;
|
||||
TaskWeight = weight;
|
||||
CurrentProgress = 0;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
double percent = ++CurrentProgress / TaskWeight * 100;
|
||||
Excel.StatusBar = $"{Message} Выполнено {percent:#.#} %";
|
||||
}
|
||||
}
|
49
RhSolutions.AddIn/Tools/ResultBar.cs
Normal file
49
RhSolutions.AddIn/Tools/ResultBar.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System.Text;
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.Tools;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
internal class ResultBar : StatusbarBase
|
||||
{
|
||||
private int Success { get; set; }
|
||||
private int Replaced { get; set; }
|
||||
private int NotFound { get; set; }
|
||||
|
||||
public ResultBar()
|
||||
{
|
||||
Success = 0;
|
||||
Replaced = 0;
|
||||
NotFound = 0;
|
||||
}
|
||||
|
||||
public void IncrementSuccess() => Success++;
|
||||
public void IncrementReplaced() => Replaced++;
|
||||
public void IncrementNotFound() => NotFound++;
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (Success > 0)
|
||||
{
|
||||
sb.Append($"Успешно экспортировано {Success} артикулов. ");
|
||||
}
|
||||
|
||||
if (Replaced > 0)
|
||||
{
|
||||
sb.Append($"Заменено {Replaced} артикулов. ");
|
||||
}
|
||||
|
||||
if (NotFound > 0)
|
||||
{
|
||||
sb.Append($"Не найдено {NotFound} артикулов.");
|
||||
}
|
||||
|
||||
Excel.StatusBar = sb.ToString();
|
||||
}
|
||||
}
|
24
RhSolutions.AddIn/Tools/StatusbarBase.cs
Normal file
24
RhSolutions.AddIn/Tools/StatusbarBase.cs
Normal file
@ -0,0 +1,24 @@
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
using RhSolutions;
|
||||
using RhSolutions.Models;
|
||||
using RhSolutions.Tools;
|
||||
#endif
|
||||
|
||||
|
||||
namespace RhSolutions.Tools;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
internal abstract class StatusbarBase : IDisposable
|
||||
{
|
||||
protected Application Excel = RhSolutionsAddIn.Excel;
|
||||
|
||||
public abstract void Update();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Excel.OnTime(DateTime.Now + new TimeSpan(0, 0, 5), "StatusBarReset");
|
||||
}
|
||||
}
|
22
RhSolutions.AddIn/Tools/ToolBase.cs
Normal file
22
RhSolutions.AddIn/Tools/ToolBase.cs
Normal file
@ -0,0 +1,22 @@
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.Tools;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
internal abstract class ToolBase
|
||||
{
|
||||
protected readonly IExcelReader _reader;
|
||||
protected readonly IExcelWriter _writer;
|
||||
|
||||
public ToolBase()
|
||||
{
|
||||
_reader = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IExcelReader>();
|
||||
_writer = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IExcelWriter>();
|
||||
}
|
||||
|
||||
public abstract void Execute();
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
global using ExcelDna.Integration;
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
global using Microsoft.Office.Interop.Excel;
|
||||
global using RhSolutions.AddIn;
|
||||
global using RhSolutions.Models;
|
||||
global using RhSolutions.Services;
|
||||
global using RhSolutions.Tools;
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Linq;
|
||||
global using Range = Microsoft.Office.Interop.Excel.Range;
|
||||
global using Range = Microsoft.Office.Interop.Excel.Range;
|
||||
|
26
RhSolutions.ExcelExtensions/Cell.cs
Normal file
26
RhSolutions.ExcelExtensions/Cell.cs
Normal file
@ -0,0 +1,26 @@
|
||||
namespace RhSolutions.ExcelExtensions;
|
||||
|
||||
public sealed class Cell
|
||||
{
|
||||
public Table ParentTable { get; }
|
||||
public Row ParentRow
|
||||
{
|
||||
get => ParentTable.Rows[ParentTable.Range.Row - _range.Row];
|
||||
}
|
||||
public Column ParentColumn
|
||||
{
|
||||
get => ParentTable.Columns[ParentTable.Range.Column - _range.Column];
|
||||
}
|
||||
public object Value
|
||||
{
|
||||
get => _range.Cells[1, 1].Value2;
|
||||
set => _range.Cells[1, 1].Value2 = value;
|
||||
}
|
||||
private Range _range;
|
||||
|
||||
public Cell(Range range, Table table)
|
||||
{
|
||||
_range = range;
|
||||
ParentTable = table;
|
||||
}
|
||||
}
|
@ -1,44 +1,62 @@
|
||||
using System.Collections;
|
||||
namespace RhSolutions.ExcelExtensions;
|
||||
|
||||
namespace RhSolutions.ExcelExtensions;
|
||||
|
||||
public sealed class Column : Table, IEnumerable<TableCell>
|
||||
public sealed class Column
|
||||
{
|
||||
public Table ParentTable { get; }
|
||||
public string Header
|
||||
{
|
||||
get => Range.Cells[1, 1].Value.ToString();
|
||||
get => _range.Cells[1, 1].Value2.ToString() ?? String.Empty;
|
||||
}
|
||||
public int Index
|
||||
{
|
||||
get => Range.Column - ParentTable.Range.Column;
|
||||
get => _range.Column - ParentTable.Range.Column;
|
||||
}
|
||||
public int Length
|
||||
{
|
||||
get => Range.Rows.Count;
|
||||
get => _range.Rows.Count;
|
||||
}
|
||||
private Cell[] _cells;
|
||||
private readonly Range _range;
|
||||
|
||||
public Column(Range range, Table table) : base(range, table)
|
||||
public Column(Range range, Table table)
|
||||
{
|
||||
Range = range;
|
||||
ParentTable = table;
|
||||
_cells = new Cell[range.Rows.Count];
|
||||
_range = range;
|
||||
ParentTable = table ??
|
||||
throw new ArgumentNullException("table");
|
||||
}
|
||||
|
||||
public TableCell this[int index]
|
||||
public Cell this[int index]
|
||||
{
|
||||
get => new(Range.Cells[index + 1, 1], ParentTable);
|
||||
get
|
||||
{
|
||||
if (_cells[index] == null)
|
||||
{
|
||||
_cells[index] = new Cell(_range.Cells[index + 1, 1], ParentTable);
|
||||
return _cells[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return _cells[index];
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_cells[index] == null)
|
||||
{
|
||||
_cells[index] = new Cell(_range.Cells[index + 1, 1], ParentTable);
|
||||
_cells[index].Value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cells[index].Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<TableCell> GetEnumerator()
|
||||
{
|
||||
return new ColumnEnumerator(Range, ParentTable);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
|
||||
public Column AddLeft()
|
||||
{
|
||||
Range.EntireColumn
|
||||
_range.EntireColumn
|
||||
.Insert(XlInsertShiftDirection.xlShiftToRight,
|
||||
XlInsertFormatOrigin.xlFormatFromRightOrBelow);
|
||||
|
||||
|
@ -1,54 +0,0 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace RhSolutions.ExcelExtensions;
|
||||
|
||||
public class ColumnEnumerator : IEnumerator<TableCell>
|
||||
{
|
||||
public Range Range { get; }
|
||||
public Table ParentTable { get; }
|
||||
private int position = 0;
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
public TableCell Current
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return new TableCell(Range.Cells[position, 1], ParentTable);
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ColumnEnumerator(Range range, Table table)
|
||||
{
|
||||
Range = range;
|
||||
ParentTable = table;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
position++;
|
||||
return (position <= Range.Rows.Count);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
position = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -4,35 +4,45 @@ namespace RhSolutions.ExcelExtensions;
|
||||
|
||||
public class Columns : IEnumerable<Column>
|
||||
{
|
||||
public Range Range { get; }
|
||||
public Table ParentTable { get; }
|
||||
public int Length
|
||||
{
|
||||
get => Range.Columns.Count;
|
||||
get => _range.Columns.Count;
|
||||
}
|
||||
private Column[] _columns;
|
||||
private Range _range;
|
||||
|
||||
public Columns(Range range, Table parentTable)
|
||||
public Columns(Table parentTable)
|
||||
{
|
||||
Range = range;
|
||||
ParentTable = parentTable;
|
||||
_range = parentTable.Range;
|
||||
_columns = new Column[Length];
|
||||
}
|
||||
|
||||
public Column this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index + 1 > Range.Columns.Count)
|
||||
if (index < 0 || index >= Length)
|
||||
{
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
|
||||
return new Column(Range.Columns[index + 1], ParentTable);
|
||||
if (_columns[index] == null)
|
||||
{
|
||||
_columns[index] = new Column(_range.Columns[index + 1], ParentTable);
|
||||
return _columns[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return _columns[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<Column> GetEnumerator()
|
||||
{
|
||||
return new ColumnsEnumerator(Range, ParentTable);
|
||||
return new ColumnsEnumerator(this);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
@ -4,9 +4,8 @@ namespace RhSolutions.ExcelExtensions;
|
||||
|
||||
public class ColumnsEnumerator: IEnumerator<Column>
|
||||
{
|
||||
public Range Range { get; }
|
||||
public Table ParentTable { get; }
|
||||
private int position = 0;
|
||||
private Columns _columns;
|
||||
private int position = -1;
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get
|
||||
@ -21,7 +20,7 @@ public class ColumnsEnumerator: IEnumerator<Column>
|
||||
{
|
||||
try
|
||||
{
|
||||
return new Column(Range.Columns[position], ParentTable);
|
||||
return _columns[position];
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
@ -30,21 +29,20 @@ public class ColumnsEnumerator: IEnumerator<Column>
|
||||
}
|
||||
}
|
||||
|
||||
public ColumnsEnumerator(Range range, Table table)
|
||||
public ColumnsEnumerator(Columns columns)
|
||||
{
|
||||
Range = range;
|
||||
ParentTable = table;
|
||||
_columns = columns;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
position++;
|
||||
return (position <= Range.Columns.Count);
|
||||
return (position < _columns.Length);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
position = 0;
|
||||
position = -1;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -1,33 +1,54 @@
|
||||
using System.Collections;
|
||||
namespace RhSolutions.ExcelExtensions;
|
||||
|
||||
namespace RhSolutions.ExcelExtensions;
|
||||
|
||||
public sealed class Row : Table, IEnumerable<TableCell>
|
||||
public sealed class Row
|
||||
{
|
||||
public Table ParentTable { get; }
|
||||
public int Index
|
||||
{
|
||||
get => Range.Row - ParentTable.Range.Row;
|
||||
get => _range.Row - ParentTable.Range.Row;
|
||||
}
|
||||
public int Length
|
||||
{
|
||||
get => Range.Columns.Count;
|
||||
get => _range.Columns.Count;
|
||||
}
|
||||
private readonly Cell[] _cells;
|
||||
private readonly Range _range;
|
||||
|
||||
public Row(Range range, Table table) : base(range, table)
|
||||
public Row(Range range, Table table)
|
||||
{
|
||||
Range = range;
|
||||
ParentTable = table;
|
||||
_cells = new Cell[range.Columns.Count];
|
||||
_range = range;
|
||||
ParentTable = table ??
|
||||
throw new ArgumentNullException("table");
|
||||
}
|
||||
|
||||
public TableCell this[int index]
|
||||
public Cell this[int index]
|
||||
{
|
||||
get => new(Range.Cells[1, index + 1], ParentTable);
|
||||
get
|
||||
{
|
||||
if (index < 0 || index >= Length)
|
||||
{
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
|
||||
if (_cells[index] == null)
|
||||
{
|
||||
_cells[index] = new Cell(_range.Cells[1, index + 1], ParentTable);
|
||||
return _cells[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return _cells[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<TableCell> GetEnumerator()
|
||||
public Cell this[string header]
|
||||
{
|
||||
return new RowEnumerator(Range, ParentTable);
|
||||
get
|
||||
{
|
||||
int columnIndex = ParentTable.ColumnByHeader(header).Index;
|
||||
return this[columnIndex];
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
@ -1,54 +0,0 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace RhSolutions.ExcelExtensions;
|
||||
|
||||
public class RowEnumerator : IEnumerator<TableCell>
|
||||
{
|
||||
public Range Range { get; }
|
||||
public Table ParentTable { get; }
|
||||
private int position = 0;
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
public TableCell Current
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return new TableCell(Range.Cells[1, position], ParentTable);
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RowEnumerator(Range range, Table parentTable)
|
||||
{
|
||||
Range = range;
|
||||
ParentTable = parentTable;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
position++;
|
||||
return (position <= Range.Columns.Count);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
position = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -4,35 +4,40 @@ namespace RhSolutions.ExcelExtensions;
|
||||
|
||||
public class Rows : IEnumerable<Row>
|
||||
{
|
||||
public Range Range { get; }
|
||||
public Table ParentTable { get; }
|
||||
public int Length
|
||||
{
|
||||
get => Range.Rows.Count;
|
||||
get => _range.Rows.Count;
|
||||
}
|
||||
private Row[] _rows;
|
||||
private Range _range;
|
||||
|
||||
public Rows(Range range, Table parentTable)
|
||||
public Rows(Table parentTable)
|
||||
{
|
||||
Range = range;
|
||||
ParentTable = parentTable;
|
||||
_range = parentTable.Range;
|
||||
_rows = new Row[Length];
|
||||
}
|
||||
|
||||
public Row this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index + 1 > Range.Rows.Count)
|
||||
if (_rows[index] == null)
|
||||
{
|
||||
throw new IndexOutOfRangeException();
|
||||
_rows[index] = new Row(_range.Rows[index + 1], ParentTable);
|
||||
return _rows[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return _rows[index];
|
||||
}
|
||||
|
||||
return new Row(Range.Rows[index + 1], ParentTable);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<Row> GetEnumerator()
|
||||
{
|
||||
return new RowsEnumerator(Range, ParentTable);
|
||||
return new RowsEnumerator(this);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
@ -4,9 +4,8 @@ namespace RhSolutions.ExcelExtensions;
|
||||
|
||||
public class RowsEnumerator : IEnumerator<Row>
|
||||
{
|
||||
public Range Range { get; }
|
||||
public Table ParentTable { get; }
|
||||
private int position = 0;
|
||||
private Rows _rows;
|
||||
private int position = -1;
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get
|
||||
@ -21,7 +20,7 @@ public class RowsEnumerator : IEnumerator<Row>
|
||||
{
|
||||
try
|
||||
{
|
||||
return new Row(Range.Rows[position], ParentTable);
|
||||
return _rows[position];
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
@ -30,26 +29,24 @@ public class RowsEnumerator : IEnumerator<Row>
|
||||
}
|
||||
}
|
||||
|
||||
public RowsEnumerator(Range range, Table table)
|
||||
public RowsEnumerator(Rows rows)
|
||||
{
|
||||
Range = range;
|
||||
ParentTable = table;
|
||||
_rows = rows;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
position++;
|
||||
return (position <= Range.Rows.Count);
|
||||
return (position < _rows.Length);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
position = 0;
|
||||
position = -1;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -6,29 +6,40 @@ public class Table
|
||||
public Table ParentTable { get; protected set; }
|
||||
public Rows Rows { get; }
|
||||
public Columns Columns { get; }
|
||||
private Dictionary<string, Column> _columnsByHeader;
|
||||
|
||||
public Table(Range range)
|
||||
{
|
||||
Range = range;
|
||||
ParentTable = null;
|
||||
Rows = new Rows(Range, this);
|
||||
Columns = new Columns(Range, this);
|
||||
ParentTable = this;
|
||||
Rows = new Rows(this);
|
||||
Columns = new Columns(this);
|
||||
_columnsByHeader = new();
|
||||
|
||||
foreach(var column in Columns)
|
||||
{
|
||||
if (_columnsByHeader.ContainsKey(column.Header))
|
||||
{
|
||||
throw new ArgumentException($"Заголовок столбца {column.Header} не уникален");
|
||||
}
|
||||
else
|
||||
{
|
||||
_columnsByHeader.Add(column.Header, column);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Table(Range range, Table table)
|
||||
public Column ColumnByHeader(string header)
|
||||
{
|
||||
Range = range;
|
||||
ParentTable = table;
|
||||
Rows = new Rows(Range, this);
|
||||
Columns = new Columns(Range, this);
|
||||
return _columnsByHeader[header];
|
||||
}
|
||||
|
||||
public TableCell this[int row, int column]
|
||||
public Cell this[int row, int column]
|
||||
{
|
||||
get => new(Range.Cells[row + 1, column + 1], this);
|
||||
get => this.Rows[row][column];
|
||||
}
|
||||
|
||||
public IEnumerable<TableCell> Find(object item)
|
||||
public IEnumerable<Cell> Search(object item)
|
||||
{
|
||||
Range firstFound = Range.Find(item);
|
||||
if (firstFound == null)
|
||||
@ -40,7 +51,7 @@ public class Table
|
||||
|
||||
while (true)
|
||||
{
|
||||
yield return new TableCell(nextFound, ParentTable ?? this);
|
||||
yield return this[nextFound.Row - ParentTable.Range.Row, nextFound.Column - ParentTable.Range.Column];
|
||||
nextFound = Range.FindNext(nextFound);
|
||||
|
||||
if (nextFound.Row == firstFound.Row
|
||||
|
@ -1,24 +0,0 @@
|
||||
namespace RhSolutions.ExcelExtensions;
|
||||
|
||||
public sealed class TableCell : Table
|
||||
{
|
||||
public Row ParentRow
|
||||
{
|
||||
get => ParentTable.Rows[ParentTable.Range.Row - Range.Row];
|
||||
}
|
||||
public Column ParentColumn
|
||||
{
|
||||
get => ParentTable.Columns[ParentTable.Range.Column - Range.Column];
|
||||
}
|
||||
public object Value
|
||||
{
|
||||
get => Range.Cells[1, 1].Value2;
|
||||
set => Range.Cells[1, 1].Value2 = value;
|
||||
}
|
||||
|
||||
public TableCell(Range range, Table table) : base(range, table)
|
||||
{
|
||||
Range = range;
|
||||
ParentTable = table;
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
namespace RhSolutions.Tests;
|
||||
|
||||
[ExcelTestSettings(OutOfProcess = true, Workbook = @"TestWorkbooks\ExcelTableTest.xlsx")]
|
||||
public class ExcelTablesTests : IDisposable
|
||||
{
|
||||
ExcelExtensions.Table table;
|
||||
|
||||
public ExcelTablesTests()
|
||||
{
|
||||
Util.Application.Workbooks.Add();
|
||||
|
||||
Worksheet worksheet = Util.Workbook.Sheets[1];
|
||||
Range range = worksheet.Range["E7:G9"];
|
||||
table = new(range);
|
||||
}
|
||||
|
||||
[ExcelFact]
|
||||
public void CanReadExcelTable()
|
||||
{
|
||||
Assert.Equal(3, table.Columns.Where(c => c.Header.StartsWith("Столбец")).Count());
|
||||
Assert.Equal("Столбец 1", table.Rows.First()[0].Value);
|
||||
Assert.Equal("Столбец 2", table[0, 1].Value);
|
||||
Assert.Equal(123d, table[1, 1].Value);
|
||||
Assert.Null(table[1, 2].Value);
|
||||
}
|
||||
|
||||
[ExcelFact]
|
||||
public void CanModifyTableCells()
|
||||
{
|
||||
table[2, 1].Value = 1;
|
||||
table[1, 1].Value = (double)table[1, 1].Value + 123;
|
||||
Assert.Equal(1d, table[2, 1].Value);
|
||||
Assert.Equal(246d, table[1, 1].Value);
|
||||
}
|
||||
|
||||
[ExcelFact]
|
||||
public void CanFindInTable()
|
||||
{
|
||||
table[2, 0].Value = "Find!";
|
||||
table[2, 1].Value = "Find this!";
|
||||
table[2, 2].Value = "Find that!";
|
||||
|
||||
|
||||
var cells = table.Find("Find");
|
||||
Assert.Collection(cells, item => Assert.Equal("Find!", item.Value),
|
||||
item => Assert.Equal("Find this!", item.Value),
|
||||
item => Assert.Equal("Find that!", item.Value));
|
||||
Assert.Equal(0, table.Find("Значение").First().ParentColumn.Index);
|
||||
Assert.Equal(3, table.Rows[2].Find("Find").Count());
|
||||
Assert.Empty(table.Columns[1].Find("Пусто"));
|
||||
}
|
||||
|
||||
[ExcelFact]
|
||||
public void CanAddColumns()
|
||||
{
|
||||
int originalCount = table.Columns.Count();
|
||||
table.Columns[1].AddLeft();
|
||||
Assert.Equal(originalCount + 1, table.Columns.Count());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Util.Application.ActiveWindow.Close(SaveChanges: false);
|
||||
}
|
||||
}
|
@ -7,8 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RhSolutions.AddIn", "RhSolu
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RhSolutions.Tests", "RhSolutions.Tests\RhSolutions.Tests.csproj", "{6EECCDDB-741C-404A-874F-BB8656265162}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RhSolutions.ExcelExtensions", "RhSolutions.ExcelExtensions\RhSolutions.ExcelExtensions.csproj", "{ADB862A8-5CC6-4509-A4F7-9907E84F5801}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -23,10 +21,6 @@ Global
|
||||
{6EECCDDB-741C-404A-874F-BB8656265162}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6EECCDDB-741C-404A-874F-BB8656265162}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6EECCDDB-741C-404A-874F-BB8656265162}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ADB862A8-5CC6-4509-A4F7-9907E84F5801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ADB862A8-5CC6-4509-A4F7-9907E84F5801}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ADB862A8-5CC6-4509-A4F7-9907E84F5801}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ADB862A8-5CC6-4509-A4F7-9907E84F5801}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
Loading…
Reference in New Issue
Block a user