Compare commits
30 Commits
2cb7cd0377
...
c638085ac7
Author | SHA1 | Date | |
---|---|---|---|
|
c638085ac7 | ||
|
c86f0b8143 | ||
|
75cb3c4f5d | ||
|
0a7d9646f3 | ||
|
dfd0db9133 | ||
|
f7eec55b4a | ||
|
b1a9959c1a | ||
|
0aa9d644df | ||
|
93195a9cc9 | ||
|
99f46e089c | ||
|
51c97607ef | ||
|
8bcc8d34d4 | ||
|
02a6c7151f | ||
|
bf97036724 | ||
|
bfd7702939 | ||
|
448af8ecd7 | ||
|
da29243d1d | ||
|
f01228d945 | ||
|
cdb153c988 | ||
|
2280b49ae1 | ||
|
de2f7f43ec | ||
|
7bb6ce6c5a | ||
|
6555d6343f | ||
|
68512dba2b | ||
|
15dd27fb68 | ||
|
9846a3c35e | ||
|
19007ff43e | ||
|
a3094fe275 | ||
|
f34690828c | ||
|
6291d30027 |
@ -1,56 +0,0 @@
|
|||||||
using ExcelDna.Integration;
|
|
||||||
using RhSolutions.Models;
|
|
||||||
using RhSolutions.Services;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace RhSolutions.AddIn
|
|
||||||
{
|
|
||||||
public class Functions
|
|
||||||
{
|
|
||||||
[ExcelFunction(Description = "Распознать артикул и попробовать найти его в прайс-листе")]
|
|
||||||
public static object RHSOLUTIONS([ExcelArgument(Name = "\"Строка с названием материала\"")] string line)
|
|
||||||
{
|
|
||||||
object result;
|
|
||||||
|
|
||||||
result = ExcelAsyncUtil.Run("Database request", line, delegate
|
|
||||||
{
|
|
||||||
return RhDatabaseClient.GetProduct(line).GetAwaiter().GetResult();
|
|
||||||
});
|
|
||||||
|
|
||||||
string parsedSku = Sku.TryParse(line, out var skus)
|
|
||||||
? skus.First().ToString() : string.Empty;
|
|
||||||
|
|
||||||
if (result == null)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(parsedSku))
|
|
||||||
{
|
|
||||||
return ExcelError.ExcelErrorNA;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return skus.First().ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.Equals(ExcelError.ExcelErrorNA))
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(parsedSku))
|
|
||||||
{
|
|
||||||
return "Загрузка...";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return skus.First().ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcelFunction]
|
|
||||||
public static void _ResetStatusBar()
|
|
||||||
{
|
|
||||||
RhSolutionsAddIn.Excel.StatusBar = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
16
RhSolutions.AddIn/AddIn/ResetBarFunction.cs
Normal file
16
RhSolutions.AddIn/AddIn/ResetBarFunction.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#if !NET472
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace RhSolutions.AddIn;
|
||||||
|
#if !NET472
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
#endif
|
||||||
|
public static class ResetBarFunction
|
||||||
|
{
|
||||||
|
[ExcelFunction]
|
||||||
|
public static void StatusBarReset()
|
||||||
|
{
|
||||||
|
RhSolutionsAddIn.Excel.StatusBar = false;
|
||||||
|
}
|
||||||
|
}
|
@ -1,35 +1,42 @@
|
|||||||
using ExcelDna.Integration;
|
using System.Net;
|
||||||
using ExcelDna.IntelliSense;
|
#if !NET472
|
||||||
using Microsoft.Office.Interop.Excel;
|
using System.Runtime.Versioning;
|
||||||
using RhSolutions.Services;
|
#endif
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
|
|
||||||
namespace RhSolutions.AddIn
|
namespace RhSolutions.AddIn;
|
||||||
|
|
||||||
|
#if !NET472
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
#endif
|
||||||
|
public sealed class RhSolutionsAddIn : IExcelAddIn
|
||||||
{
|
{
|
||||||
class RhSolutionsAddIn : IExcelAddIn
|
public static Application Excel { get; private set; }
|
||||||
|
public static ServiceProvider ServiceProvider { get; private set; }
|
||||||
|
public static IAddInConfiguration Configuration { get; private set; }
|
||||||
|
|
||||||
|
public void AutoOpen()
|
||||||
{
|
{
|
||||||
public static Application Excel { get; private set; }
|
IServiceCollection Services = new ServiceCollection();
|
||||||
public static HttpClient HttpClient { get; private set; }
|
|
||||||
|
|
||||||
public void AutoOpen()
|
Services.AddHttpClient()
|
||||||
{
|
.AddSingleton((Application)ExcelDnaUtil.Application)
|
||||||
Excel = (Application)ExcelDnaUtil.Application;
|
.AddSingleton<IDatabaseClient, RhDatabaseClient>()
|
||||||
HttpClient = new HttpClient();
|
.AddSingleton<IAddInConfiguration, RhAddInConfiguration>()
|
||||||
IntelliSenseServer.Install();
|
.AddTransient<IFileDialog, ExcelFileDialog>()
|
||||||
RegistryUtil.Initialize();
|
.AddTransient<IExcelReader, RhExcelReader>()
|
||||||
EventsUtil.Initialize();
|
.AddTransient<IExcelWriter, RhExcelWriter>();
|
||||||
|
|
||||||
ServicePointManager.SecurityProtocol =
|
ServiceProvider = Services.BuildServiceProvider();
|
||||||
SecurityProtocolType.Tls12;
|
Configuration = ServiceProvider.GetService<IAddInConfiguration>();
|
||||||
}
|
Excel = ServiceProvider.GetService<Application>();
|
||||||
|
|
||||||
public void AutoClose()
|
EventsUtil.Initialize();
|
||||||
{
|
ServicePointManager.SecurityProtocol =
|
||||||
IntelliSenseServer.Uninstall();
|
SecurityProtocolType.Tls12;
|
||||||
RegistryUtil.Uninitialize();
|
}
|
||||||
EventsUtil.Uninitialize();
|
|
||||||
HttpClient.Dispose();
|
public void AutoClose()
|
||||||
}
|
{
|
||||||
|
EventsUtil.Uninitialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
55
RhSolutions.AddIn/AddIn/RhSolutionsFunction.cs
Normal file
55
RhSolutions.AddIn/AddIn/RhSolutionsFunction.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#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>();
|
||||||
|
|
||||||
|
Sku.TryParse(line, out var skus);
|
||||||
|
|
||||||
|
if (ExcelAsyncUtil.Run("Database request", line, delegate
|
||||||
|
{
|
||||||
|
return databaseClient.GetProducts(line)
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
|
}) is not IEnumerable<Product> requestResult)
|
||||||
|
{
|
||||||
|
if (skus.Any())
|
||||||
|
{
|
||||||
|
return $"{skus.First()} ...";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Загрузка...";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!requestResult.Any() && !skus.Any())
|
||||||
|
{
|
||||||
|
return ExcelError.ExcelErrorNA;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (!requestResult.Any())
|
||||||
|
{
|
||||||
|
return $"{skus.First()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var firstProduct = requestResult.First();
|
||||||
|
return $"{firstProduct.ProductSku} {firstProduct.Name}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,57 +0,0 @@
|
|||||||
using Microsoft.Office.Interop.Excel;
|
|
||||||
using RhSolutions.Models;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Dialog = RhSolutions.Models.Dialog;
|
|
||||||
using Range = Microsoft.Office.Interop.Excel.Range;
|
|
||||||
|
|
||||||
namespace RhSolutions.Controllers
|
|
||||||
{
|
|
||||||
internal class CombineTool : ToolBase
|
|
||||||
{
|
|
||||||
private List<SourcePriceList> SourceFiles { get; set; }
|
|
||||||
|
|
||||||
public CombineTool()
|
|
||||||
{
|
|
||||||
string[] files = Dialog.GetMultiplyFiles();
|
|
||||||
|
|
||||||
if (files != null)
|
|
||||||
{
|
|
||||||
SourceFiles = SourcePriceList.GetSourceLists(files);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,30 +0,0 @@
|
|||||||
using RhSolutions.Models;
|
|
||||||
|
|
||||||
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,101 +0,0 @@
|
|||||||
using Microsoft.Office.Interop.Excel;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using RhSolutions.Models;
|
|
||||||
using System.Linq;
|
|
||||||
using Range = Microsoft.Office.Interop.Excel.Range;
|
|
||||||
|
|
||||||
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,46 +0,0 @@
|
|||||||
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()
|
|
||||||
{
|
|
||||||
string[] files = Dialog.GetMultiplyFiles();
|
|
||||||
|
|
||||||
if (files != null)
|
|
||||||
{
|
|
||||||
SourceFiles = SourcePriceList.GetSourceLists(files);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,24 +1,24 @@
|
|||||||
using ExcelDna.Integration.CustomUI;
|
using ExcelDna.Integration.CustomUI;
|
||||||
using Microsoft.Office.Interop.Excel;
|
|
||||||
using RhSolutions.AddIn;
|
|
||||||
using RhSolutions.Services;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
#if! NET472
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
#endif
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Range = Microsoft.Office.Interop.Excel.Range;
|
|
||||||
|
|
||||||
namespace RhSolutions.Controllers
|
namespace RhSolutions.Controllers;
|
||||||
|
|
||||||
|
#if !NET472
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
#endif
|
||||||
|
[ComVisible(true)]
|
||||||
|
public class RibbonController : ExcelRibbon
|
||||||
{
|
{
|
||||||
[ComVisible(true)]
|
private static IRibbonUI ribbonUi;
|
||||||
public class RibbonController : ExcelRibbon
|
|
||||||
{
|
|
||||||
private static IRibbonUI ribbonUi;
|
|
||||||
|
|
||||||
public override string GetCustomUI(string RibbonID)
|
public override string GetCustomUI(string RibbonID)
|
||||||
{
|
{
|
||||||
return @"
|
return @"
|
||||||
<customUI onLoad='RibbonLoad' xmlns='http://schemas.microsoft.com/office/2006/01/customui'>
|
<customUI onLoad='RibbonLoad' xmlns='http://schemas.microsoft.com/office/2006/01/customui'>
|
||||||
<ribbon>
|
<ribbon>
|
||||||
<tabs>
|
<tabs>
|
||||||
@ -26,10 +26,7 @@ namespace RhSolutions.Controllers
|
|||||||
<group id='priceList' label='Прайс-лист'>
|
<group id='priceList' label='Прайс-лист'>
|
||||||
<button id='export' getEnabled='GetExportEnabled' label='Экспорт в новый файл' size='normal' imageMso='PivotExportToExcel' onAction='OnToolPressed'/>
|
<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='convert' getEnabled='GetConvertEnabled' label='Актуализировать' size='normal' imageMso='FileUpdate' onAction='OnToolPressed'/>
|
||||||
<menu id='conjoinMenu' label='Объединить' imageMso='Copy'>
|
<button id='merge' label='Объединить' size='normal' imageMso='Copy' onAction='OnToolPressed'/>
|
||||||
<button id='merge' label='Сложить' onAction='OnToolPressed'/>
|
|
||||||
<button id='combine' label='По колонкам' onAction='OnToolPressed'/>
|
|
||||||
</menu>
|
|
||||||
</group>
|
</group>
|
||||||
<group id='rausettings' getLabel='GetVersionLabel'>
|
<group id='rausettings' getLabel='GetVersionLabel'>
|
||||||
<button id='setPriceList' getLabel='GetPriceListPathLabel' size='large' imageMso='TableExcelSpreadsheetInsert' onAction='OnSetPricePressed'/>
|
<button id='setPriceList' getLabel='GetPriceListPathLabel' size='large' imageMso='TableExcelSpreadsheetInsert' onAction='OnSetPricePressed'/>
|
||||||
@ -38,102 +35,88 @@ namespace RhSolutions.Controllers
|
|||||||
</tabs>
|
</tabs>
|
||||||
</ribbon>
|
</ribbon>
|
||||||
</customUI>";
|
</customUI>";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RibbonLoad(IRibbonUI sender)
|
public void RibbonLoad(IRibbonUI sender)
|
||||||
|
{
|
||||||
|
ribbonUi = sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RefreshControl(string id)
|
||||||
|
{
|
||||||
|
ribbonUi?.InvalidateControl(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnSetPricePressed(IRibbonControl control)
|
||||||
|
{
|
||||||
|
IFileDialog dialog = RhSolutionsAddIn.ServiceProvider.GetService<IFileDialog>();
|
||||||
|
string file = dialog.GetFile();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(file))
|
||||||
{
|
{
|
||||||
ribbonUi = sender;
|
RhSolutionsAddIn.Configuration.SetPriceListPath(file);
|
||||||
}
|
RhSolutionsAddIn.Configuration.SaveSettings();
|
||||||
|
|
||||||
public static void RefreshControl(string id)
|
|
||||||
{
|
|
||||||
if (ribbonUi != null)
|
|
||||||
{
|
|
||||||
ribbonUi.InvalidateControl(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void OnSetPricePressed(IRibbonControl control)
|
|
||||||
{
|
|
||||||
string path = Models.Dialog.GetFilePath();
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
RegistryUtil.PriceListPath = path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnToolPressed(IRibbonControl control)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ToolBase tool;
|
|
||||||
switch (control.Id)
|
|
||||||
{
|
|
||||||
case "export":
|
|
||||||
tool = new ExportTool();
|
|
||||||
break;
|
|
||||||
case "convert":
|
|
||||||
tool = new ConvertTool();
|
|
||||||
break;
|
|
||||||
case "merge":
|
|
||||||
tool = new MergeTool();
|
|
||||||
break;
|
|
||||||
case "combine":
|
|
||||||
tool = new CombineTool();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Неизвестный инструмент");
|
|
||||||
}
|
|
||||||
|
|
||||||
tool.OpenNewPrice();
|
|
||||||
tool.FillTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
MessageBox.Show(exception.Message,
|
|
||||||
"Ошибка",
|
|
||||||
MessageBoxButtons.OK,
|
|
||||||
MessageBoxIcon.Information);
|
|
||||||
RhSolutionsAddIn.Excel.StatusBar = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetConvertEnabled(IRibbonControl control)
|
|
||||||
{
|
|
||||||
if (RhSolutionsAddIn.Excel.ActiveWorkbook == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Worksheet worksheet = RhSolutionsAddIn.Excel.ActiveWorkbook.ActiveSheet;
|
|
||||||
return worksheet.IsRehauSource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetExportEnabled(IRibbonControl control)
|
|
||||||
{
|
|
||||||
if (RhSolutionsAddIn.Excel.ActiveWorkbook == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Range selection = RhSolutionsAddIn.Excel.Selection;
|
|
||||||
return selection.Columns.Count == 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetVersionLabel(IRibbonControl control)
|
|
||||||
{
|
|
||||||
string version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
|
||||||
return $"v{version}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetPriceListPathLabel(IRibbonControl control)
|
|
||||||
{
|
|
||||||
string name = RegistryUtil.GetPriceListName();
|
|
||||||
return string.IsNullOrEmpty(name) ? "Нет файла шаблона!" : name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnToolPressed(IRibbonControl control)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using ToolBase tool = control.Id switch
|
||||||
|
{
|
||||||
|
"export" => new ExportTool(),
|
||||||
|
"convert" => new ConvertTool(),
|
||||||
|
"merge" => new MergeTool(),
|
||||||
|
_ => throw new Exception("Неизвестный инструмент"),
|
||||||
|
};
|
||||||
|
tool.Execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
MessageBox.Show(exception.Message,
|
||||||
|
"Ошибка",
|
||||||
|
MessageBoxButtons.OK,
|
||||||
|
MessageBoxIcon.Information);
|
||||||
|
RhSolutionsAddIn.Excel.StatusBar = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetConvertEnabled(IRibbonControl control)
|
||||||
|
{
|
||||||
|
if (RhSolutionsAddIn.Excel.ActiveWorkbook == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Worksheet worksheet = RhSolutionsAddIn.Excel.ActiveWorkbook.ActiveSheet;
|
||||||
|
return worksheet.IsValidSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetExportEnabled(IRibbonControl control)
|
||||||
|
{
|
||||||
|
if (RhSolutionsAddIn.Excel.ActiveWorkbook == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Range selection = RhSolutionsAddIn.Excel.Selection;
|
||||||
|
return selection.Columns.Count == 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetVersionLabel(IRibbonControl control)
|
||||||
|
{
|
||||||
|
string version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||||
|
return $"v{version}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetPriceListPathLabel(IRibbonControl control)
|
||||||
|
{
|
||||||
|
string name = RhSolutionsAddIn.Configuration.GetPriceListFileName();
|
||||||
|
return string.IsNullOrEmpty(name) ? "Шаблонный файл" : name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,190 +0,0 @@
|
|||||||
using Microsoft.Office.Interop.Excel;
|
|
||||||
using RhSolutions.AddIn;
|
|
||||||
using RhSolutions.Models;
|
|
||||||
using RhSolutions.Services;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Range = Microsoft.Office.Interop.Excel.Range;
|
|
||||||
|
|
||||||
namespace RhSolutions.Controllers
|
|
||||||
{
|
|
||||||
internal abstract class ToolBase
|
|
||||||
{
|
|
||||||
protected Application ExcelApp = RhSolutionsAddIn.Excel;
|
|
||||||
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 == RegistryUtil.PriceListPath) != null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Шаблонный файл редактируется в другом месте");
|
|
||||||
}
|
|
||||||
|
|
||||||
Workbook wb = ExcelApp.Workbooks.Open(RegistryUtil.PriceListPath, 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,40 +0,0 @@
|
|||||||
using Microsoft.Office.Interop.Excel;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
|
|
||||||
namespace RhSolutions.Models
|
|
||||||
{
|
|
||||||
static class Dialog
|
|
||||||
{
|
|
||||||
public static string GetFilePath()
|
|
||||||
{
|
|
||||||
using (OpenFileDialog dialog = new OpenFileDialog())
|
|
||||||
{
|
|
||||||
dialog.Filter = "Файлы Excel (*.xls;*.xlsx;*.xlsm)|*.xls;*.xlsx;*.xlsm";
|
|
||||||
|
|
||||||
if (dialog.ShowDialog() == DialogResult.OK)
|
|
||||||
{
|
|
||||||
return dialog.FileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
else return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string[] GetMultiplyFiles()
|
|
||||||
{
|
|
||||||
using (OpenFileDialog dialog = new OpenFileDialog())
|
|
||||||
{
|
|
||||||
dialog.Filter = "Файлы Excel (*.xls;*.xlsx;*.xlsm)|*.xls;*.xlsx;*.xlsm";
|
|
||||||
dialog.Multiselect = true;
|
|
||||||
|
|
||||||
if (dialog.ShowDialog() == DialogResult.OK)
|
|
||||||
{
|
|
||||||
return dialog.FileNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
else return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,11 +0,0 @@
|
|||||||
namespace RhSolutions.Models
|
|
||||||
{
|
|
||||||
internal static class PriceListHeaders
|
|
||||||
{
|
|
||||||
public static readonly string Amount = "Кол-во";
|
|
||||||
public static readonly string OldSku = "Прежний материал";
|
|
||||||
public static readonly string Sku = "Актуальный материал";
|
|
||||||
public static readonly string Group = "Программа";
|
|
||||||
public static readonly string Name = "Наименование";
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,116 +0,0 @@
|
|||||||
using ExcelDna.Integration;
|
|
||||||
using Microsoft.Office.Interop.Excel;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using Range = Microsoft.Office.Interop.Excel.Range;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
Range[] cells = new[]
|
|
||||||
{
|
|
||||||
AmountCell = Sheet.Cells.Find(PriceListHeaders.Amount),
|
|
||||||
SkuCell = Sheet.Cells.Find(PriceListHeaders.Sku),
|
|
||||||
GroupCell = Sheet.Cells.Find(PriceListHeaders.Group),
|
|
||||||
NameCell = Sheet.Cells.Find(PriceListHeaders.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,41 +0,0 @@
|
|||||||
using Microsoft.Office.Interop.Excel;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using Range = Microsoft.Office.Interop.Excel.Range;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
Range[] cells = new[]
|
|
||||||
{
|
|
||||||
AmountCell = Sheet.Cells.Find(PriceListHeaders.Amount),
|
|
||||||
SkuCell = Sheet.Cells.Find(PriceListHeaders.Sku),
|
|
||||||
GroupCell = Sheet.Cells.Find(PriceListHeaders.Group),
|
|
||||||
NameCell = Sheet.Cells.Find(PriceListHeaders.Name)
|
|
||||||
};
|
|
||||||
|
|
||||||
OldSkuCell = Sheet.Cells.Find(PriceListHeaders.OldSku);
|
|
||||||
|
|
||||||
if (cells.Any(x => x == null))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Шаблон {Name} не является прайс-листом");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
|||||||
using Microsoft.Office.Interop.Excel;
|
|
||||||
using RhSolutions.Models;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace RhSolutions.Services
|
|
||||||
{
|
|
||||||
public static class WorksheetExtensions
|
|
||||||
{
|
|
||||||
public static bool IsRehauSource(this Worksheet worksheet)
|
|
||||||
{
|
|
||||||
Range amountCell;
|
|
||||||
Range skuCell;
|
|
||||||
Range groupCell;
|
|
||||||
Range nameCell;
|
|
||||||
|
|
||||||
Range[] cells = new[]
|
|
||||||
{
|
|
||||||
amountCell = worksheet.Cells.Find(PriceListHeaders.Amount),
|
|
||||||
skuCell = worksheet.Cells.Find(PriceListHeaders.Sku),
|
|
||||||
groupCell = worksheet.Cells.Find(PriceListHeaders.Group),
|
|
||||||
nameCell = worksheet.Cells.Find(PriceListHeaders.Name)
|
|
||||||
};
|
|
||||||
|
|
||||||
return cells.All(x => x != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddValue(this Range range, double value)
|
|
||||||
{
|
|
||||||
if (range.Value2 == null)
|
|
||||||
{
|
|
||||||
range.Value2 = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
range.Value2 += value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
// General Information about an assembly is controlled through the following
|
||||||
// set of attributes. Change these attribute values to modify the information
|
// set of attributes. Change these attribute values to modify the information
|
||||||
@ -33,5 +31,5 @@ using System.Runtime.Versioning;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.2.4.0")]
|
[assembly: AssemblyVersion("1.5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("1.2.4.0")]
|
[assembly: AssemblyFileVersion("1.5.0.0")]
|
||||||
|
@ -1,26 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<DnaLibrary Name="RhSolutions Add-In" RuntimeVersion="v4.0" xmlns="http://schemas.excel-dna.net/addin/2020/07/dnalibrary">
|
<DnaLibrary Name="RhSolutions Add-In" RuntimeVersion="v4.0" xmlns="http://schemas.excel-dna.net/addin/2020/07/dnalibrary">
|
||||||
<ExternalLibrary Path="RhSolutions.AddIn.dll" ExplicitExports="false" LoadFromBytes="true" Pack="true" IncludePdb="false" />
|
<ExternalLibrary Path="RhSolutions.AddIn.dll" ExplicitExports="false" LoadFromBytes="true" Pack="true" IncludePdb="false" />
|
||||||
<Reference Path="ExcelDna.IntelliSense.dll" Pack="true" />
|
<Reference Path="Microsoft.Bcl.AsyncInterfaces.dll" Pack="true" />
|
||||||
|
<Reference Path="Microsoft.Bcl.HashCode.dll" Pack="true" />
|
||||||
|
<Reference Path="Microsoft.Extensions.DependencyInjection.Abstractions.dll" Pack="true" />
|
||||||
|
<Reference Path="Microsoft.Extensions.DependencyInjection.dll" Pack="true" />
|
||||||
|
<Reference Path="Microsoft.Extensions.Http.dll" Pack="true" />
|
||||||
|
<Reference Path="Microsoft.Extensions.Logging.Abstractions.dll" Pack="true" />
|
||||||
|
<Reference Path="Microsoft.Extensions.Logging.dll" Pack="true" />
|
||||||
|
<Reference Path="Microsoft.Extensions.Options.dll" Pack="true" />
|
||||||
|
<Reference Path="Microsoft.Extensions.Primitives.dll" Pack="true" />
|
||||||
<Reference Path="Newtonsoft.Json.dll" Pack="true" />
|
<Reference Path="Newtonsoft.Json.dll" Pack="true" />
|
||||||
<Reference Path="RhSolutions.Sku.dll" Pack="true" />
|
<Reference Path="RhSolutions.Sku.dll" Pack="true" />
|
||||||
|
<Reference Path="System.Buffers.dll" Pack="true" />
|
||||||
|
<Reference Path="System.Diagnostics.DiagnosticSource.dll" Pack="true" />
|
||||||
<!--
|
<Reference Path="System.Memory.dll" Pack="true" />
|
||||||
The RuntimeVersion attribute above allows only the following setting:
|
<Reference Path="System.Numerics.Vectors.dll" Pack="true" />
|
||||||
* RuntimeVersion="v4.0" - for .NET 4.5 or higher
|
<Reference Path="System.Runtime.CompilerServices.Unsafe.dll" Pack="true" />
|
||||||
|
<Reference Path="System.Threading.Tasks.Extensions.dll" Pack="true" />
|
||||||
You can have IntelliSense (autocomplete) and validation for this file.
|
<Reference Path="System.ValueTuple.dll" Pack="true" />
|
||||||
See https://github.com/Excel-DNA/ExcelDna/tree/master/Distribution/XmlSchemas/
|
|
||||||
|
|
||||||
Additional referenced assemblies can be specified by adding 'Reference' tags.
|
|
||||||
These libraries will not be examined and registered with Excel as add-in libraries,
|
|
||||||
but will be packed into the -packed.xll file and loaded at runtime as needed.
|
|
||||||
For example:
|
|
||||||
|
|
||||||
<Reference Path="Another.Library.dll" Pack="true" />
|
|
||||||
|
|
||||||
Excel-DNA also allows the XML for ribbon UI extensions to be specified in the .dna file.
|
|
||||||
See the main Excel-DNA site at https://excel-dna.net for downloads of the full distribution.
|
|
||||||
-->
|
|
||||||
</DnaLibrary>
|
</DnaLibrary>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net472;net6.0-windows</TargetFrameworks>
|
<TargetFrameworks>net472</TargetFrameworks>
|
||||||
<LangVersion>10</LangVersion>
|
<LangVersion>10</LangVersion>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<RootNamespace>RhSolutions.AddIn</RootNamespace>
|
<RootNamespace>RhSolutions</RootNamespace>
|
||||||
<AssemblyName>RhSolutions.AddIn</AssemblyName>
|
<AssemblyName>RhSolutions.AddIn</AssemblyName>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<UseWindowsForms>true</UseWindowsForms>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
@ -12,14 +12,31 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net472|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net472|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net6.0-windows7.0|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net6.0-windows7.0|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ExcelDna.AddIn" Version="1.6.0" />
|
<PackageReference Include="ExcelDna.AddIn" Version="1.6.0">
|
||||||
|
<TreatAsUsed>true</TreatAsUsed>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="ExcelDna.Integration" Version="1.6.0" />
|
<PackageReference Include="ExcelDna.Integration" Version="1.6.0" />
|
||||||
<PackageReference Include="ExcelDna.IntelliSense" Version="1.6.0" />
|
|
||||||
<PackageReference Include="ExcelDna.Interop" Version="15.0.1" />
|
<PackageReference Include="ExcelDna.Interop" Version="15.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
|
||||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="RhSolutions.Sku" Version="0.1.1" />
|
<PackageReference Include="RhSolutions.Sku" Version="0.1.1" />
|
||||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
||||||
|
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -1,35 +0,0 @@
|
|||||||
using Microsoft.Office.Interop.Excel;
|
|
||||||
using RhSolutions.AddIn;
|
|
||||||
using RhSolutions.Controllers;
|
|
||||||
|
|
||||||
namespace RhSolutions.Services
|
|
||||||
{
|
|
||||||
internal static class EventsUtil
|
|
||||||
{
|
|
||||||
private static readonly Application Excel = RhSolutionsAddIn.Excel;
|
|
||||||
|
|
||||||
public static void Initialize()
|
|
||||||
{
|
|
||||||
Excel.SheetSelectionChange += RefreshExportButton;
|
|
||||||
Excel.SheetActivate += RefreshConvertButton;
|
|
||||||
Excel.WorkbookActivate += RefreshConvertButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Uninitialize()
|
|
||||||
{
|
|
||||||
Excel.SheetSelectionChange -= RefreshExportButton;
|
|
||||||
Excel.SheetActivate -= RefreshConvertButton;
|
|
||||||
Excel.WorkbookActivate -= RefreshConvertButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RefreshConvertButton(object sh)
|
|
||||||
{
|
|
||||||
RibbonController.RefreshControl("convert");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RefreshExportButton(object sh, Range target)
|
|
||||||
{
|
|
||||||
RibbonController.RefreshControl("export");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
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>();
|
||||||
|
}
|
||||||
|
}
|
13
RhSolutions.AddIn/Services/IAddInConfiguration.cs
Normal file
13
RhSolutions.AddIn/Services/IAddInConfiguration.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System.Configuration;
|
||||||
|
|
||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public interface IAddInConfiguration
|
||||||
|
{
|
||||||
|
public string GetPriceListPath();
|
||||||
|
public string GetPriceListFileName();
|
||||||
|
public Dictionary<string, string> GetPriceListHeaders();
|
||||||
|
public event SettingChangingEventHandler OnSettingsChange;
|
||||||
|
public void SetPriceListPath(string value);
|
||||||
|
public void SaveSettings();
|
||||||
|
}
|
11
RhSolutions.AddIn/Services/IDatabaseClient.cs
Normal file
11
RhSolutions.AddIn/Services/IDatabaseClient.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using RhSolutions.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RhSolutions.Services
|
||||||
|
{
|
||||||
|
public interface IDatabaseClient
|
||||||
|
{
|
||||||
|
public Task<IEnumerable<Product>> GetProducts(string query);
|
||||||
|
}
|
||||||
|
}
|
9
RhSolutions.AddIn/Services/IExcelReader.cs
Normal file
9
RhSolutions.AddIn/Services/IExcelReader.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public interface IExcelReader : IDisposable
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
new void Dispose();
|
||||||
|
}
|
8
RhSolutions.AddIn/Services/IExcelWriter.cs
Normal file
8
RhSolutions.AddIn/Services/IExcelWriter.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public interface IExcelWriter : IDisposable
|
||||||
|
{
|
||||||
|
public void WriteProducts(IEnumerable<(string, Dictionary<Product, double>)> products);
|
||||||
|
public void WriteProducts(Dictionary<Product, double> products);
|
||||||
|
new void Dispose();
|
||||||
|
}
|
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();
|
||||||
|
}
|
@ -1,74 +0,0 @@
|
|||||||
using Microsoft.Win32;
|
|
||||||
using RhSolutions.Controllers;
|
|
||||||
using RhSolutions.Models;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
|
|
||||||
namespace RhSolutions.Services
|
|
||||||
{
|
|
||||||
static class RegistryUtil
|
|
||||||
{
|
|
||||||
private static string priceListPath;
|
|
||||||
private static RegistryKey RootKey { get; set; }
|
|
||||||
|
|
||||||
public static void Initialize()
|
|
||||||
{
|
|
||||||
RootKey = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\REHAU\SkuAssist");
|
|
||||||
priceListPath = RootKey.GetValue("PriceListPath") as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Uninitialize()
|
|
||||||
{
|
|
||||||
RootKey.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string PriceListPath
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(priceListPath) || !File.Exists(priceListPath))
|
|
||||||
{
|
|
||||||
DialogResult result = MessageBox.Show("Прайс-лист отсутствует или неверный файл шаблона прайс-листа. " +
|
|
||||||
"Укажите файл шаблона прайс-листа.",
|
|
||||||
"Нет файла шаблона",
|
|
||||||
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
||||||
|
|
||||||
if (result == DialogResult.OK)
|
|
||||||
{
|
|
||||||
string fileName = Dialog.GetFilePath();
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(fileName))
|
|
||||||
{
|
|
||||||
throw new Exception("Нет файла шаблона");
|
|
||||||
}
|
|
||||||
|
|
||||||
priceListPath = fileName;
|
|
||||||
RootKey.SetValue("PriceListPath", fileName);
|
|
||||||
return priceListPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
throw new Exception("Нет файла шаблона");
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return priceListPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
priceListPath = value;
|
|
||||||
RootKey.SetValue("PriceListPath", value);
|
|
||||||
RibbonController.RefreshControl("setPriceList");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetPriceListName()
|
|
||||||
{
|
|
||||||
return Path.GetFileName(priceListPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
103
RhSolutions.AddIn/Services/RhAddInConfiguration.cs
Normal file
103
RhSolutions.AddIn/Services/RhAddInConfiguration.cs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
using System.Configuration;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public class RhAddInConfiguration : ApplicationSettingsBase, IAddInConfiguration
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, string> _priceListHeaders;
|
||||||
|
|
||||||
|
public RhAddInConfiguration()
|
||||||
|
{
|
||||||
|
_priceListHeaders = new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
["Amount"] = AmountHeader,
|
||||||
|
["OldSku"] = OldSkuHeader,
|
||||||
|
["Sku"] = SkuHeader,
|
||||||
|
["ProductLine"] = ProductLineHeader,
|
||||||
|
["Name"] = NameHeader
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[UserScopedSetting]
|
||||||
|
[DefaultSettingValue("Кол-во")]
|
||||||
|
public string AmountHeader
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (string)this[nameof(AmountHeader)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[UserScopedSetting]
|
||||||
|
[DefaultSettingValue("Прежний материал")]
|
||||||
|
public string OldSkuHeader
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (string)this[nameof(OldSkuHeader)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[UserScopedSetting]
|
||||||
|
[DefaultSettingValue("Актуальный материал")]
|
||||||
|
public string SkuHeader
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (string)this[nameof(SkuHeader)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[UserScopedSetting]
|
||||||
|
[DefaultSettingValue("Программа")]
|
||||||
|
public string ProductLineHeader
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (string)this[nameof(ProductLineHeader)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[UserScopedSetting]
|
||||||
|
[DefaultSettingValue("Наименование")]
|
||||||
|
public string NameHeader
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (string)this[nameof(NameHeader)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[UserScopedSetting]
|
||||||
|
public string PriceListPath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (string)this[nameof(PriceListPath)];
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this[nameof(PriceListPath)] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event SettingChangingEventHandler OnSettingsChange
|
||||||
|
{
|
||||||
|
add
|
||||||
|
{
|
||||||
|
base.SettingChanging += value;
|
||||||
|
}
|
||||||
|
remove
|
||||||
|
{
|
||||||
|
base.SettingChanging -= value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetPriceListFileName() => Path.GetFileName(PriceListPath);
|
||||||
|
public string GetPriceListPath() => PriceListPath;
|
||||||
|
public void SetPriceListPath(string value) => PriceListPath = value;
|
||||||
|
public void SaveSettings() => base.Save();
|
||||||
|
public Dictionary<string, string> GetPriceListHeaders() => _priceListHeaders;
|
||||||
|
}
|
@ -1,52 +1,49 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using RhSolutions.AddIn;
|
using System.Net;
|
||||||
using RhSolutions.Models;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace RhSolutions.Services
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public class RhDatabaseClient : IDatabaseClient
|
||||||
{
|
{
|
||||||
public static class RhDatabaseClient
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
public HttpStatusCode StatusCode { get; private set; }
|
||||||
|
|
||||||
|
public RhDatabaseClient(IServiceProvider provider)
|
||||||
{
|
{
|
||||||
public static async Task<object> GetProduct(string line)
|
this.serviceProvider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Product>> GetProducts(string line)
|
||||||
|
{
|
||||||
|
string request;
|
||||||
|
|
||||||
|
if (Sku.TryParse(line, out var skus))
|
||||||
{
|
{
|
||||||
string request;
|
request = @"https://rh.cebotari.ru/api/products/" + skus.FirstOrDefault().ToString();
|
||||||
|
|
||||||
if (Sku.TryParse(line, out var skus))
|
|
||||||
{
|
|
||||||
request = @"https://rh.cebotari.ru/api/products/" + skus.FirstOrDefault().ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
request = @"https://rh.cebotari.ru/api/search?query=" + line;
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await RhSolutionsAddIn.HttpClient.GetAsync(request);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
string json = await response.Content.ReadAsStringAsync();
|
|
||||||
var product = JsonConvert.DeserializeObject<IEnumerable<Product>>(json)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (product == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return $"{product.ProductSku} {product.Name}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return $"Ошибка сервера {response.StatusCode}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request = @"https://rh.cebotari.ru/api/search?query=" + line;
|
||||||
|
}
|
||||||
|
|
||||||
|
var client = serviceProvider.GetRequiredService<HttpClient>();
|
||||||
|
var response = await client.GetAsync(request);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
string json = await response.Content.ReadAsStringAsync();
|
||||||
|
return JsonConvert.DeserializeObject<IEnumerable<Product>>(json) ?? Enumerable.Empty<Product>();
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
StatusCode = response.StatusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enumerable.Empty<Product>();
|
||||||
}
|
}
|
||||||
}
|
}
|
168
RhSolutions.AddIn/Services/RhExcelReader.cs
Normal file
168
RhSolutions.AddIn/Services/RhExcelReader.cs
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
using System.IO;
|
||||||
|
#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.IsValidSource())
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
255
RhSolutions.AddIn/Services/RhExcelWriter.cs
Normal file
255
RhSolutions.AddIn/Services/RhExcelWriter.cs
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
#if !NET472
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
using RhSolutions.Tools;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
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.IsValidSource())
|
||||||
|
{
|
||||||
|
_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();
|
||||||
|
}
|
||||||
|
}
|
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);
|
||||||
|
}
|
||||||
|
}
|
47
RhSolutions.AddIn/Tools/EventsUtil.cs
Normal file
47
RhSolutions.AddIn/Tools/EventsUtil.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using Microsoft.Office.Interop.Excel;
|
||||||
|
using RhSolutions.AddIn;
|
||||||
|
using RhSolutions.Controllers;
|
||||||
|
using System.Configuration;
|
||||||
|
#if !NET472
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace RhSolutions.Tools
|
||||||
|
{
|
||||||
|
#if !NET472
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
#endif
|
||||||
|
internal static class EventsUtil
|
||||||
|
{
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
RhSolutionsAddIn.Excel.SheetSelectionChange += RefreshExportButton;
|
||||||
|
RhSolutionsAddIn.Excel.SheetActivate += RefreshConvertButton;
|
||||||
|
RhSolutionsAddIn.Excel.WorkbookActivate += RefreshConvertButton;
|
||||||
|
RhSolutionsAddIn.Configuration.OnSettingsChange += RefreshSettingTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Uninitialize()
|
||||||
|
{
|
||||||
|
RhSolutionsAddIn.Excel.SheetSelectionChange -= RefreshExportButton;
|
||||||
|
RhSolutionsAddIn.Excel.SheetActivate -= RefreshConvertButton;
|
||||||
|
RhSolutionsAddIn.Excel.WorkbookActivate -= RefreshConvertButton;
|
||||||
|
RhSolutionsAddIn.Configuration.OnSettingsChange -= RefreshSettingTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RefreshConvertButton(object sh)
|
||||||
|
{
|
||||||
|
RibbonController.RefreshControl("convert");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RefreshExportButton(object sh, Range target)
|
||||||
|
{
|
||||||
|
RibbonController.RefreshControl("export");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RefreshSettingTitle(object sender, SettingChangingEventArgs e)
|
||||||
|
{
|
||||||
|
RibbonController.RefreshControl("setPriceList");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
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");
|
||||||
|
}
|
||||||
|
}
|
28
RhSolutions.AddIn/Tools/ToolBase.cs
Normal file
28
RhSolutions.AddIn/Tools/ToolBase.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#if !NET472
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace RhSolutions.Tools;
|
||||||
|
|
||||||
|
#if !NET472
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
#endif
|
||||||
|
internal abstract class ToolBase : IDisposable
|
||||||
|
{
|
||||||
|
protected readonly IExcelReader _reader;
|
||||||
|
protected readonly IExcelWriter _writer;
|
||||||
|
|
||||||
|
public ToolBase()
|
||||||
|
{
|
||||||
|
_reader = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IExcelReader>();
|
||||||
|
_writer = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IExcelWriter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_reader.Dispose();
|
||||||
|
_writer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Execute();
|
||||||
|
}
|
46
RhSolutions.AddIn/Tools/WorksheetExtensions.cs
Normal file
46
RhSolutions.AddIn/Tools/WorksheetExtensions.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#if !NET472
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace RhSolutions.Tools;
|
||||||
|
|
||||||
|
#if !NET472
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
#endif
|
||||||
|
public static class WorksheetExtensions
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<string, string> pricelistParameters =
|
||||||
|
RhSolutionsAddIn.Configuration.GetPriceListHeaders();
|
||||||
|
|
||||||
|
public static bool IsValidSource(this Worksheet worksheet)
|
||||||
|
{
|
||||||
|
Range amountCell;
|
||||||
|
Range skuCell;
|
||||||
|
Range programLineCell;
|
||||||
|
Range nameCell;
|
||||||
|
|
||||||
|
Range[] cells = new[]
|
||||||
|
{
|
||||||
|
amountCell = worksheet.Cells.Find(pricelistParameters["Amount"]),
|
||||||
|
skuCell = worksheet.Cells.Find(pricelistParameters["Sku"]),
|
||||||
|
programLineCell = worksheet.Cells.Find(pricelistParameters["ProductLine"]),
|
||||||
|
nameCell = worksheet.Cells.Find(pricelistParameters["Name"])
|
||||||
|
};
|
||||||
|
|
||||||
|
return cells.All(x => x != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddValue(this Range range, double value)
|
||||||
|
{
|
||||||
|
if (range.Value2 == null)
|
||||||
|
{
|
||||||
|
range.Value2 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
range.Value2 += value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
11
RhSolutions.AddIn/Usings.cs
Normal file
11
RhSolutions.AddIn/Usings.cs
Normal file
@ -0,0 +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;
|
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;
|
||||||
|
}
|
||||||
|
}
|
65
RhSolutions.ExcelExtensions/Column.cs
Normal file
65
RhSolutions.ExcelExtensions/Column.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
namespace RhSolutions.ExcelExtensions;
|
||||||
|
|
||||||
|
public sealed class Column
|
||||||
|
{
|
||||||
|
public Table ParentTable { get; }
|
||||||
|
public string Header
|
||||||
|
{
|
||||||
|
get => _range.Cells[1, 1].Value2.ToString() ?? String.Empty;
|
||||||
|
}
|
||||||
|
public int Index
|
||||||
|
{
|
||||||
|
get => _range.Column - ParentTable.Range.Column;
|
||||||
|
}
|
||||||
|
public int Length
|
||||||
|
{
|
||||||
|
get => _range.Rows.Count;
|
||||||
|
}
|
||||||
|
private Cell[] _cells;
|
||||||
|
private readonly Range _range;
|
||||||
|
|
||||||
|
public Column(Range range, Table table)
|
||||||
|
{
|
||||||
|
_cells = new Cell[range.Rows.Count];
|
||||||
|
_range = range;
|
||||||
|
ParentTable = table ??
|
||||||
|
throw new ArgumentNullException("table");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cell this[int index]
|
||||||
|
{
|
||||||
|
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 Column AddLeft()
|
||||||
|
{
|
||||||
|
_range.EntireColumn
|
||||||
|
.Insert(XlInsertShiftDirection.xlShiftToRight,
|
||||||
|
XlInsertFormatOrigin.xlFormatFromRightOrBelow);
|
||||||
|
|
||||||
|
return ParentTable.Columns[this.Index - 1];
|
||||||
|
}
|
||||||
|
}
|
49
RhSolutions.ExcelExtensions/Columns.cs
Normal file
49
RhSolutions.ExcelExtensions/Columns.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace RhSolutions.ExcelExtensions;
|
||||||
|
|
||||||
|
public class Columns : IEnumerable<Column>
|
||||||
|
{
|
||||||
|
public Table ParentTable { get; }
|
||||||
|
public int Length
|
||||||
|
{
|
||||||
|
get => _range.Columns.Count;
|
||||||
|
}
|
||||||
|
private Column[] _columns;
|
||||||
|
private Range _range;
|
||||||
|
|
||||||
|
public Columns(Table parentTable)
|
||||||
|
{
|
||||||
|
ParentTable = parentTable;
|
||||||
|
_range = parentTable.Range;
|
||||||
|
_columns = new Column[Length];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Column this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= Length)
|
||||||
|
{
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
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(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
52
RhSolutions.ExcelExtensions/ColumnsEnumerator.cs
Normal file
52
RhSolutions.ExcelExtensions/ColumnsEnumerator.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace RhSolutions.ExcelExtensions;
|
||||||
|
|
||||||
|
public class ColumnsEnumerator: IEnumerator<Column>
|
||||||
|
{
|
||||||
|
private Columns _columns;
|
||||||
|
private int position = -1;
|
||||||
|
object IEnumerator.Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Column Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _columns[position];
|
||||||
|
}
|
||||||
|
catch (IndexOutOfRangeException)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColumnsEnumerator(Columns columns)
|
||||||
|
{
|
||||||
|
_columns = columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
position++;
|
||||||
|
return (position < _columns.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
position = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>net472;net6.0-windows7.0</TargetFrameworks>
|
||||||
|
<LangVersion>10</LangVersion>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ExcelDna.Interop" Version="15.0.1" />
|
||||||
|
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
54
RhSolutions.ExcelExtensions/Row.cs
Normal file
54
RhSolutions.ExcelExtensions/Row.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
namespace RhSolutions.ExcelExtensions;
|
||||||
|
|
||||||
|
public sealed class Row
|
||||||
|
{
|
||||||
|
public Table ParentTable { get; }
|
||||||
|
public int Index
|
||||||
|
{
|
||||||
|
get => _range.Row - ParentTable.Range.Row;
|
||||||
|
}
|
||||||
|
public int Length
|
||||||
|
{
|
||||||
|
get => _range.Columns.Count;
|
||||||
|
}
|
||||||
|
private readonly Cell[] _cells;
|
||||||
|
private readonly Range _range;
|
||||||
|
|
||||||
|
public Row(Range range, Table table)
|
||||||
|
{
|
||||||
|
_cells = new Cell[range.Columns.Count];
|
||||||
|
_range = range;
|
||||||
|
ParentTable = table ??
|
||||||
|
throw new ArgumentNullException("table");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cell this[int index]
|
||||||
|
{
|
||||||
|
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 Cell this[string header]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int columnIndex = ParentTable.ColumnByHeader(header).Index;
|
||||||
|
return this[columnIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
RhSolutions.ExcelExtensions/Rows.cs
Normal file
44
RhSolutions.ExcelExtensions/Rows.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace RhSolutions.ExcelExtensions;
|
||||||
|
|
||||||
|
public class Rows : IEnumerable<Row>
|
||||||
|
{
|
||||||
|
public Table ParentTable { get; }
|
||||||
|
public int Length
|
||||||
|
{
|
||||||
|
get => _range.Rows.Count;
|
||||||
|
}
|
||||||
|
private Row[] _rows;
|
||||||
|
private Range _range;
|
||||||
|
|
||||||
|
public Rows(Table parentTable)
|
||||||
|
{
|
||||||
|
ParentTable = parentTable;
|
||||||
|
_range = parentTable.Range;
|
||||||
|
_rows = new Row[Length];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Row this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_rows[index] == null)
|
||||||
|
{
|
||||||
|
_rows[index] = new Row(_range.Rows[index + 1], ParentTable);
|
||||||
|
return _rows[index];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return _rows[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<Row> GetEnumerator()
|
||||||
|
{
|
||||||
|
return new RowsEnumerator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
52
RhSolutions.ExcelExtensions/RowsEnumerator.cs
Normal file
52
RhSolutions.ExcelExtensions/RowsEnumerator.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace RhSolutions.ExcelExtensions;
|
||||||
|
|
||||||
|
public class RowsEnumerator : IEnumerator<Row>
|
||||||
|
{
|
||||||
|
private Rows _rows;
|
||||||
|
private int position = -1;
|
||||||
|
object IEnumerator.Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Row Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _rows[position];
|
||||||
|
}
|
||||||
|
catch (IndexOutOfRangeException)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public RowsEnumerator(Rows rows)
|
||||||
|
{
|
||||||
|
_rows = rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
position++;
|
||||||
|
return (position < _rows.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
position = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
64
RhSolutions.ExcelExtensions/Table.cs
Normal file
64
RhSolutions.ExcelExtensions/Table.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
namespace RhSolutions.ExcelExtensions;
|
||||||
|
|
||||||
|
public class Table
|
||||||
|
{
|
||||||
|
public Range Range { get; protected set; }
|
||||||
|
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 = 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 Column ColumnByHeader(string header)
|
||||||
|
{
|
||||||
|
return _columnsByHeader[header];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cell this[int row, int column]
|
||||||
|
{
|
||||||
|
get => this.Rows[row][column];
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Cell> Search(object item)
|
||||||
|
{
|
||||||
|
Range firstFound = Range.Find(item);
|
||||||
|
if (firstFound == null)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Range nextFound = firstFound;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
yield return this[nextFound.Row - ParentTable.Range.Row, nextFound.Column - ParentTable.Range.Column];
|
||||||
|
nextFound = Range.FindNext(nextFound);
|
||||||
|
|
||||||
|
if (nextFound.Row == firstFound.Row
|
||||||
|
&& nextFound.Column == firstFound.Column)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
RhSolutions.ExcelExtensions/Usings.cs
Normal file
3
RhSolutions.ExcelExtensions/Usings.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
global using Microsoft.Office.Interop.Excel;
|
||||||
|
global using System.Collections.Generic;
|
||||||
|
global using Range = Microsoft.Office.Interop.Excel.Range;
|
63
RhSolutions.Tests/CanReadProducts.cs
Normal file
63
RhSolutions.Tests/CanReadProducts.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using RhSolutions.AddIn;
|
||||||
|
|
||||||
|
namespace RhSolutions.Tests;
|
||||||
|
|
||||||
|
[ExcelTestSettings(OutOfProcess = true)]
|
||||||
|
public class CanReadProducts : IDisposable
|
||||||
|
{
|
||||||
|
private RhSolutionsAddIn _addIn;
|
||||||
|
private IExcelReader _reader;
|
||||||
|
private Workbook _testWorkbook;
|
||||||
|
|
||||||
|
public CanReadProducts()
|
||||||
|
{
|
||||||
|
_addIn = new();
|
||||||
|
_testWorkbook = Util.Application.Workbooks.Add();
|
||||||
|
_addIn.AutoOpen();
|
||||||
|
_reader = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IExcelReader>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact]
|
||||||
|
public void CanReadRange()
|
||||||
|
{
|
||||||
|
Worksheet worksheet = _testWorkbook.Sheets[1];
|
||||||
|
worksheet.Range["A1"].Value = "11600011001";
|
||||||
|
worksheet.Range["A2"].Value = "11600011001";
|
||||||
|
worksheet.Range["A3"].Value = "160002-001";
|
||||||
|
worksheet.Range["A4"].Value = "Fuzz";
|
||||||
|
worksheet.Range["B1"].Value = 10;
|
||||||
|
worksheet.Range["B2"].Value = 10;
|
||||||
|
worksheet.Range["B3"].Value = 5;
|
||||||
|
worksheet.Range["B5"].Value = 1000_000;
|
||||||
|
worksheet.Range["A6"].Value = "111111-111";
|
||||||
|
worksheet.Range["B6"].Value = 100;
|
||||||
|
|
||||||
|
Range testRange = worksheet.Range["A1:B6"];
|
||||||
|
|
||||||
|
var products = _reader.ReadProducts(testRange);
|
||||||
|
|
||||||
|
Assert.NotNull(products);
|
||||||
|
Assert.NotEmpty(products);
|
||||||
|
Assert.Equal("11600011001", products.First().Key.ProductSku);
|
||||||
|
Assert.Equal(20.0, products.First().Value);
|
||||||
|
Assert.Equal(125.0, products.Sum(p => p.Value));
|
||||||
|
Assert.Equal(3, products.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"TestWorkbooks\Specifications\HeatingFloor.xlsx")]
|
||||||
|
public void CanReadWorkbook()
|
||||||
|
{
|
||||||
|
Worksheet worksheet = Util.Workbook.Worksheets[1];
|
||||||
|
var products = _reader.ReadProducts(new[] { worksheet });
|
||||||
|
|
||||||
|
Assert.NotNull(products);
|
||||||
|
Assert.NotEmpty(products);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_addIn.AutoClose();
|
||||||
|
Util.Application.ActiveWindow.Close(SaveChanges: false);
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,12 @@
|
|||||||
<None Update="TestWorkbooks\EmptyWorkbook.xlsx">
|
<None Update="TestWorkbooks\EmptyWorkbook.xlsx">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="TestWorkbooks\ExcelTableTest.xlsx">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="TestWorkbooks\Specifications\HeatingFloor.xlsx">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
namespace RhSolutions.Tests;
|
|
||||||
|
|
||||||
[ExcelTestSettings(OutOfProcess = true)]
|
|
||||||
public class WorkbookCheck : IDisposable
|
|
||||||
{
|
|
||||||
public WorkbookCheck()
|
|
||||||
{
|
|
||||||
Util.Application.Workbooks.Add();
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcelFact(Workbook = @"TestWorkbooks\EmptyTestTable.xlsx")]
|
|
||||||
public void WorksheetIsCorrect()
|
|
||||||
{
|
|
||||||
Worksheet worksheet= Util.Workbook.Sheets[1];
|
|
||||||
Assert.True(worksheet.IsRehauSource());
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcelFact(Workbook = @"TestWorkbooks\EmptyWorkbook.xlsx")]
|
|
||||||
public void EmptyWorkbookIsNotCorrect()
|
|
||||||
{
|
|
||||||
Worksheet worksheet = Util.Workbook.Sheets[1];
|
|
||||||
Assert.False(worksheet.IsRehauSource());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Util.Application.ActiveWorkbook.Close(SaveChanges: false);
|
|
||||||
}
|
|
||||||
}
|
|
BIN
RhSolutions.Tests/TestWorkbooks/ExcelTableTest.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/ExcelTableTest.xlsx
Normal file
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/Specifications/HeatingFloor.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/Specifications/HeatingFloor.xlsx
Normal file
Binary file not shown.
@ -1,5 +1,4 @@
|
|||||||
global using Xunit;
|
global using ExcelDna.Testing;
|
||||||
global using Microsoft.Office.Interop.Excel;
|
global using Microsoft.Office.Interop.Excel;
|
||||||
global using ExcelDna.Testing;
|
|
||||||
global using RhSolutions.Models;
|
|
||||||
global using RhSolutions.Services;
|
global using RhSolutions.Services;
|
||||||
|
global using Xunit;
|
||||||
|
37
RhSolutions.Tests/WorkbookValidationTests.cs
Normal file
37
RhSolutions.Tests/WorkbookValidationTests.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using RhSolutions.AddIn;
|
||||||
|
using RhSolutions.Tools;
|
||||||
|
|
||||||
|
namespace RhSolutions.Tests;
|
||||||
|
|
||||||
|
[ExcelTestSettings(OutOfProcess = true)]
|
||||||
|
public class WorkbookValidationTests : IDisposable
|
||||||
|
{
|
||||||
|
private readonly RhSolutionsAddIn _addIn;
|
||||||
|
|
||||||
|
public WorkbookValidationTests()
|
||||||
|
{
|
||||||
|
_addIn = new RhSolutionsAddIn();
|
||||||
|
_addIn.AutoOpen();
|
||||||
|
Util.Application.Workbooks.Add();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"TestWorkbooks\EmptyTestTable.xlsx")]
|
||||||
|
public void WorksheetIsCorrect()
|
||||||
|
{
|
||||||
|
Worksheet worksheet = Util.Workbook.Sheets[1];
|
||||||
|
Assert.True(worksheet.IsValidSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"TestWorkbooks\EmptyWorkbook.xlsx")]
|
||||||
|
public void EmptyWorkbookIsNotCorrect()
|
||||||
|
{
|
||||||
|
Worksheet worksheet = Util.Workbook.Sheets[1];
|
||||||
|
Assert.False(worksheet.IsValidSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_addIn.AutoClose();
|
||||||
|
Util.Application.ActiveWindow.Close(SaveChanges: false);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user