Compare commits
168 Commits
Author | SHA1 | Date | |
---|---|---|---|
a065c4c699 | |||
a73b2441b3 | |||
06e47d3135 | |||
5c50e6bc8e | |||
87f030e0f9 | |||
6d3f2bf55c | |||
8cb8f58714 | |||
c8a5824add | |||
38011f165e | |||
46b8468628 | |||
7a7c703aa7 | |||
3b3e37b1c3 | |||
a6e5ed1476 | |||
dfe4ef7b5b | |||
dc1e8616a6 | |||
e71636f5f8 | |||
a6a0f104c5 | |||
cc32e4792c | |||
678bdb47b9 | |||
0292348f03 | |||
9b205a5b3e | |||
5c8c5f5736 | |||
339cd65dee | |||
b931809534 | |||
964bb01a80 | |||
f0fca06beb | |||
ed47fd38f6 | |||
556bb3778e | |||
d71ad65c80 | |||
2def24c9af | |||
56112eae51 | |||
c6f6551e4e | |||
84001e050c | |||
7b2a5955d8 | |||
fe58c854ea | |||
822398e286 | |||
634f91dd13 | |||
03146a6561 | |||
17e1a1681e | |||
e73c97e1f8 | |||
61f679f52e | |||
3043756014 | |||
af2363b011 | |||
3996b2175a | |||
52f128c8b7 | |||
a8c192848d | |||
f448cccbeb | |||
e6546254ba | |||
02d0b21a58 | |||
9fd1fd8266 | |||
1b72c00b1e | |||
6d6e91867c | |||
4bc663c2ab | |||
8a8fc397bf | |||
d999a0b98a | |||
24dcec3039 | |||
199540e530 | |||
e462add7f3 | |||
2a0430b7ec | |||
2cb6b889b0 | |||
f19f8c6510 | |||
e14d714811 | |||
47c3b19a17 | |||
1f5aebe62a | |||
159ac0ec57 | |||
|
d6de208df4 | ||
|
c1949a2159 | ||
|
6fdc410c8f | ||
|
cd657e10b4 | ||
|
e4aa8dff93 | ||
a2165e7ef6 | |||
|
5fbfd3732f | ||
|
9f4823378f | ||
|
6a5038246f | ||
|
c17771a5d3 | ||
|
101c1d4397 | ||
|
2224a98d32 | ||
|
0da818da3e | ||
|
5153e951e4 | ||
|
b9cdad649a | ||
|
2c44c5d2df | ||
|
a9d7ac710e | ||
|
a73cc46449 | ||
|
63a962f495 | ||
|
8c134644ba | ||
|
f570dd9a2d | ||
|
dc92388748 | ||
|
98bb135b62 | ||
|
f89a3eb765 | ||
|
7b8cab3e9e | ||
|
50543ae697 | ||
|
a44f450df6 | ||
|
213592b9e2 | ||
|
1399d71f3c | ||
|
bfab7eb02b | ||
|
8b125e475e | ||
|
0e15ad7cd4 | ||
|
f6534fc8c7 | ||
|
51636ca61a | ||
|
f1abb03ac9 | ||
|
656f565152 | ||
|
ab217c9052 | ||
|
65d027179c | ||
|
f620147515 | ||
|
e67d94f199 | ||
|
2aae1805c5 | ||
|
cf472b66a4 | ||
|
f58dec38a4 | ||
|
9c3c65a4f8 | ||
|
3e4a5e6563 | ||
4434da98be | |||
|
1c655f1ab5 | ||
|
1220999bf0 | ||
|
1e64511ae1 | ||
|
3ecfc82b7e | ||
|
aa4c949270 | ||
|
d058b3ed4e | ||
|
79eeb2a206 | ||
|
a9aa1f30c5 | ||
|
7f8455d868 | ||
|
9d2f386a74 | ||
|
9840e0fb24 | ||
|
7fd139a234 | ||
|
9d1c213979 | ||
|
fba2859b84 | ||
|
28c5a69797 | ||
|
c0c656c82c | ||
|
a56300a620 | ||
|
16b5ddedb1 | ||
|
733440392e | ||
|
136aa7f238 | ||
|
d894c50ca3 | ||
|
c308e72ee0 | ||
|
21fb58744c | ||
|
7a7cdaab58 | ||
|
5720d2ede0 | ||
|
dcd42a32eb | ||
|
0fcc632024 | ||
|
58f5c5ba4a | ||
|
de73af8b7d | ||
|
d4ef078b9d | ||
|
fbb36fb00d | ||
|
06b397cebf | ||
|
1d64c8839d | ||
|
71a0745c70 | ||
|
d6a78c42c7 | ||
|
08fcc33f9d | ||
|
f9ff1e55b9 | ||
|
ea69a77a0e | ||
|
28d6c3b6b9 | ||
|
8902d51c14 | ||
|
5babd30c03 | ||
|
49efdad03e | ||
|
58a45df8cf | ||
|
c5895f78c2 | ||
|
9f0ef90005 | ||
|
e203155474 | ||
|
8dc186c7aa | ||
|
dcbf7d6d7f | ||
|
aca468c015 | ||
|
ade18e5e40 | ||
|
b06df453d2 | ||
|
786a5bfe26 | ||
|
46e547e3e2 | ||
|
2178c1bea3 | ||
|
627195fafe | ||
|
05914bf519 | ||
|
d4d98ec850 |
27
.vscode/launch.json
vendored
Normal file
27
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": ".NET Core Launch (console)",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build",
|
||||||
|
"program": "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE",
|
||||||
|
"args": [
|
||||||
|
"${workspaceFolder}\\RhSolutions.AddIn\\bin\\Debug\\net6.0-windows\\RhSolutions-AddIn64.xll"
|
||||||
|
],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"console": "internalConsole",
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"requireExactSource": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".NET Core Attach",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "attach"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
12
.vscode/tasks.json
vendored
Normal file
12
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "dotnet",
|
||||||
|
"task": "build ${workspaceFolder}\\RhSolutions.AddIn\\RhSolutions.AddIn.csproj",
|
||||||
|
"group": "build",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"label": "build"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
15
README.md
15
README.md
@ -3,15 +3,24 @@
|
|||||||
Помощь в обработке клиентских заявок путем поиска продукции РЕХАУ по запросу, выраженному в свободной форме, и в работе с прайс-листом BS РЕХАУ
|
Помощь в обработке клиентских заявок путем поиска продукции РЕХАУ по запросу, выраженному в свободной форме, и в работе с прайс-листом BS РЕХАУ
|
||||||
|
|
||||||
## Реализованные функции
|
## Реализованные функции
|
||||||
- Поиск артикула с помощью `=RHSOLUTIONS()`
|
|
||||||
- Экспорт массива ячеек вида "Артикул - Количество" в прайс-лист
|
- Экспорт массива ячеек вида "Артикул - Количество" в прайс-лист
|
||||||
- Актуализация прайс-листа до последней версии
|
- Актуализация прайс-листа до последней версии
|
||||||
- Объединение нескольких прайс-листов в один файл
|
- Объединение нескольких прайс-листов в один файл
|
||||||
- Сложением всех позиций по артикулам
|
- Поиск пар артикул-количество в любой сторонней таблице и экспорт в таблицу заказов
|
||||||
- С разнесением данных по колонкам в конечном файле
|
- Подбор монтажных гильз для фитингов RAUTITAN
|
||||||
|
- Подбор ремонтных муфт по количеству трубы
|
||||||
|
- Экспорт таблицы заказов в девятиграфку по ГОСТ
|
||||||
|
|
||||||
*Для работы функций "Экспорт", "Актуализация" и "Объединение" требуется указать путь к файлу пустого прайс-листа РЕХАУ*
|
*Для работы функций "Экспорт", "Актуализация" и "Объединение" требуется указать путь к файлу пустого прайс-листа РЕХАУ*
|
||||||
|
|
||||||
|
## Реализованные формулы для работы с артикулами
|
||||||
|
- ```=РЕХАУ()``` - поиск артикула РЕХАУ по произвольному запросу в сервисе [RhSolutions-Api](https://gitea.cebotari.ru/chebser/RhSolutions-Api)
|
||||||
|
- ```=РЕХАУАРТИКУЛ()``` - экстракция артикула РЕХАУ из любой строки по регулярному выражению
|
||||||
|
- ```=РЕХАУИМЯ()``` - поиск названия артикула по номеру
|
||||||
|
- ```=РЕХАУЦЕНА()``` - поиск цены в евро по номеру артикула
|
||||||
|
- ```=РЕХАУЦЕНАРУБ()``` - поиск цены в рублях по номеру артикула и курсу ЦБ на любую дату
|
||||||
|
- ```=КУРСЕВРО()``` - курс евро ЦБ на дату
|
||||||
|
|
||||||
## Установка
|
## Установка
|
||||||
1. Скопировать файл плагина в папку
|
1. Скопировать файл плагина в папку
|
||||||
```
|
```
|
||||||
|
27
RhSolutions.AddIn/AddIn/CurrencyFunctions.cs
Normal file
27
RhSolutions.AddIn/AddIn/CurrencyFunctions.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace RhSolutions.AddIn;
|
||||||
|
|
||||||
|
public static class CurrencyFunctions
|
||||||
|
{
|
||||||
|
private static readonly ICurrencyClient currencyClient =
|
||||||
|
RhSolutionsAddIn.ServiceProvider.GetRequiredService<ICurrencyClient>();
|
||||||
|
|
||||||
|
[ExcelFunction(Name = "КУРСЕВРО")]
|
||||||
|
public static object GetEuroCurrencyRate(double dateField)
|
||||||
|
{
|
||||||
|
DateTime date = dateField == 0 ? DateTime.Today : DateTime.FromOADate(dateField);
|
||||||
|
var functionName = nameof(GetEuroCurrencyRate);
|
||||||
|
var parameters = new object[] { date };
|
||||||
|
|
||||||
|
var exchangeRate = ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
|
||||||
|
{
|
||||||
|
var requestResult = await currencyClient.GetExchangeRate(date); return requestResult ?? -1m;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exchangeRate is not decimal)
|
||||||
|
{
|
||||||
|
return "Загрузка...";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (decimal)exchangeRate < 0 ? ExcelError.ExcelErrorNA : exchangeRate;
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
#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,13 +1,7 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
#if !NET472
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace RhSolutions.AddIn;
|
namespace RhSolutions.AddIn;
|
||||||
|
|
||||||
#if !NET472
|
|
||||||
[SupportedOSPlatform("windows")]
|
|
||||||
#endif
|
|
||||||
public sealed class RhSolutionsAddIn : IExcelAddIn
|
public sealed class RhSolutionsAddIn : IExcelAddIn
|
||||||
{
|
{
|
||||||
public static Application Excel { get; private set; }
|
public static Application Excel { get; private set; }
|
||||||
@ -19,17 +13,32 @@ public sealed class RhSolutionsAddIn : IExcelAddIn
|
|||||||
IServiceCollection Services = new ServiceCollection();
|
IServiceCollection Services = new ServiceCollection();
|
||||||
|
|
||||||
Services.AddHttpClient()
|
Services.AddHttpClient()
|
||||||
|
.AddMemoryCache()
|
||||||
.AddSingleton((Application)ExcelDnaUtil.Application)
|
.AddSingleton((Application)ExcelDnaUtil.Application)
|
||||||
.AddSingleton<IAddInConfiguration, AddInConfiguration>()
|
.AddSingleton<IAddInConfiguration, AddInConfiguration>()
|
||||||
.AddSingleton<IDatabaseClient, DatabaseClient>()
|
.AddSingleton<IDatabaseClient, DatabaseClient>()
|
||||||
.AddTransient<IFileDialog, FileDialog>()
|
.AddSingleton<ICurrencyClient, CurrencyClient>()
|
||||||
.AddTransient<IReader, ExcelReader>();
|
.AddTransient<IFileDialog, FileDialog>();
|
||||||
|
|
||||||
Services.AddSingleton<WriterFactory>();
|
Services.AddSingleton<WriterFactory>();
|
||||||
Services.AddTransient<ExcelWriter>()
|
Services.AddTransient<NewPriceWriter>()
|
||||||
.AddTransient<IWriter, ExcelWriter>(s => s.GetService<ExcelWriter>());
|
.AddTransient<IWriter, NewPriceWriter>(s => s.GetService<NewPriceWriter>());
|
||||||
Services.AddTransient<DxfWriter>()
|
Services.AddTransient<DxfWriter>()
|
||||||
.AddTransient<IWriter, DxfWriter>(s => s.GetService<DxfWriter>());
|
.AddTransient<IWriter, DxfWriter>(s => s.GetService<DxfWriter>());
|
||||||
|
Services.AddTransient<CurrentPriceWriter>()
|
||||||
|
.AddTransient<IWriter, CurrentPriceWriter>(s => s.GetService<CurrentPriceWriter>());
|
||||||
|
|
||||||
|
Services.AddSingleton<ReaderFactory>();
|
||||||
|
Services.AddTransient<ExcelReader>()
|
||||||
|
.AddTransient<IReader, ExcelReader>(s => s.GetService<ExcelReader>());
|
||||||
|
Services.AddTransient<GuessReader>()
|
||||||
|
.AddTransient<IReader, GuessReader>(s => s.GetService<GuessReader>());
|
||||||
|
|
||||||
|
Services.AddSingleton<FittingsCalculatorFactory>();
|
||||||
|
Services.AddTransient<CouplingsCalculator>()
|
||||||
|
.AddTransient<IFittingsCalculator, CouplingsCalculator>(s => s.GetService<CouplingsCalculator>());
|
||||||
|
Services.AddTransient<SleevesCalculator>()
|
||||||
|
.AddTransient<IFittingsCalculator, SleevesCalculator>(s => s.GetService<SleevesCalculator>());
|
||||||
|
|
||||||
Services.AddSingleton<ToolFactory>();
|
Services.AddSingleton<ToolFactory>();
|
||||||
|
|
||||||
@ -38,8 +47,10 @@ public sealed class RhSolutionsAddIn : IExcelAddIn
|
|||||||
Excel = ServiceProvider.GetService<Application>();
|
Excel = ServiceProvider.GetService<Application>();
|
||||||
|
|
||||||
EventsUtil.Initialize();
|
EventsUtil.Initialize();
|
||||||
|
|
||||||
ServicePointManager.SecurityProtocol =
|
ServicePointManager.SecurityProtocol =
|
||||||
SecurityProtocolType.Tls12;
|
SecurityProtocolType.Tls12;
|
||||||
|
ServicePointManager.DefaultConnectionLimit = 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AutoClose()
|
public void AutoClose()
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
#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>();
|
|
||||||
|
|
||||||
ProductSku.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}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[ExcelFunction]
|
|
||||||
public static object РЕХАУ(string line) => RHSOLUTIONS(line);
|
|
||||||
[ExcelFunction]
|
|
||||||
public static object РЕХАУАРТИКУЛ(string line)
|
|
||||||
{
|
|
||||||
if (ProductSku.TryParse(line, out var skus))
|
|
||||||
{
|
|
||||||
return skus.First().Id;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ExcelError.ExcelErrorNA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[ExcelFunction]
|
|
||||||
public static object РЕХАУИМЯ(string line)
|
|
||||||
{
|
|
||||||
if (!ProductSku.TryParse(line, out var skus))
|
|
||||||
{
|
|
||||||
return ExcelError.ExcelErrorNA;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var article = skus.First().Id;
|
|
||||||
IDatabaseClient databaseClient = RhSolutionsAddIn.ServiceProvider.GetService<IDatabaseClient>();
|
|
||||||
|
|
||||||
if (ExcelAsyncUtil.Run("Database request", line, delegate
|
|
||||||
{
|
|
||||||
return databaseClient.GetProducts(article)
|
|
||||||
.GetAwaiter()
|
|
||||||
.GetResult();
|
|
||||||
}) is not IEnumerable<Product> requestResult)
|
|
||||||
{
|
|
||||||
return "Загрузка...";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!requestResult.Any())
|
|
||||||
{
|
|
||||||
return ExcelError.ExcelErrorNA;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var firstProduct = requestResult.First();
|
|
||||||
return firstProduct.Name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[ExcelFunction]
|
|
||||||
public static object РЕХАУЦЕНА(string line)
|
|
||||||
{
|
|
||||||
if (!ProductSku.TryParse(line, out var skus))
|
|
||||||
{
|
|
||||||
return ExcelError.ExcelErrorNA;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var article = skus.First().Id;
|
|
||||||
IDatabaseClient databaseClient = RhSolutionsAddIn.ServiceProvider.GetService<IDatabaseClient>();
|
|
||||||
|
|
||||||
if (ExcelAsyncUtil.Run("Database request", line, delegate
|
|
||||||
{
|
|
||||||
return databaseClient.GetProducts(article)
|
|
||||||
.GetAwaiter()
|
|
||||||
.GetResult();
|
|
||||||
}) is not IEnumerable<Product> requestResult)
|
|
||||||
{
|
|
||||||
return "Загрузка...";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!requestResult.Any())
|
|
||||||
{
|
|
||||||
return ExcelError.ExcelErrorNA;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var firstProduct = requestResult.First();
|
|
||||||
return string.Format("{0:N2} €", firstProduct.Price * 1.2m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
156
RhSolutions.AddIn/AddIn/RhSolutionsFunctions.cs
Normal file
156
RhSolutions.AddIn/AddIn/RhSolutionsFunctions.cs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
namespace RhSolutions.AddIn;
|
||||||
|
|
||||||
|
public static class RhSolutionsFunctions
|
||||||
|
{
|
||||||
|
private static readonly IDatabaseClient databaseClient =
|
||||||
|
RhSolutionsAddIn.ServiceProvider.GetService<IDatabaseClient>();
|
||||||
|
private static readonly ICurrencyClient currencyClient =
|
||||||
|
RhSolutionsAddIn.ServiceProvider.GetRequiredService<ICurrencyClient>();
|
||||||
|
|
||||||
|
[ExcelFunction(Name = "РЕХАУ")]
|
||||||
|
public static object ProductSearch(object[,] values)
|
||||||
|
{
|
||||||
|
List<string> strings = new();
|
||||||
|
int rows = values.GetLength(0);
|
||||||
|
int columns = values.GetLength(1);
|
||||||
|
for (int row = 0; row < rows; row++)
|
||||||
|
{
|
||||||
|
for (int column = 0; column < columns; column++)
|
||||||
|
{
|
||||||
|
object value = values[row, column];
|
||||||
|
strings.Add(value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string query = string.Join(" ", strings.ToArray());
|
||||||
|
var functionName = nameof(ProductSearch);
|
||||||
|
var parameters = new object[] { query };
|
||||||
|
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
|
||||||
|
{
|
||||||
|
return await databaseClient.GetProducts(query);
|
||||||
|
}) is not IEnumerable<Product> products)
|
||||||
|
{
|
||||||
|
return "Загрузка...";
|
||||||
|
}
|
||||||
|
else if (!products.Any())
|
||||||
|
{
|
||||||
|
return ExcelError.ExcelErrorNA;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var product = products.First();
|
||||||
|
return $"{product.Id} {product.Name}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFunction(Name = "РЕХАУАРТИКУЛ")]
|
||||||
|
public static object SkuSearch(string query)
|
||||||
|
{
|
||||||
|
if (ProductSku.TryParse(query, out var skus))
|
||||||
|
{
|
||||||
|
return skus.First().Id;
|
||||||
|
}
|
||||||
|
return ExcelError.ExcelErrorNA;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFunction(Name = "РЕХАУИМЯ")]
|
||||||
|
public static object GetProductName(string query)
|
||||||
|
{
|
||||||
|
if (!ProductSku.TryParse(query, out var skus))
|
||||||
|
{
|
||||||
|
return ExcelError.ExcelErrorNA;
|
||||||
|
}
|
||||||
|
var article = skus.First().Id;
|
||||||
|
var functionName = nameof(GetProductName);
|
||||||
|
var parameters = new object[] { query };
|
||||||
|
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
|
||||||
|
{
|
||||||
|
return await databaseClient.GetProducts(article);
|
||||||
|
}) is not IEnumerable<Product> requestResult)
|
||||||
|
{
|
||||||
|
return "Загрузка...";
|
||||||
|
}
|
||||||
|
else if (!requestResult.Any())
|
||||||
|
{
|
||||||
|
return ExcelError.ExcelErrorNA;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var firstProduct = requestResult.First();
|
||||||
|
return firstProduct.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFunction(Name = "РЕХАУЦЕНА")]
|
||||||
|
public static object GetProductPrice(string query)
|
||||||
|
{
|
||||||
|
if (!ProductSku.TryParse(query, out var skus))
|
||||||
|
{
|
||||||
|
return ExcelError.ExcelErrorNA;
|
||||||
|
}
|
||||||
|
var article = skus.First().Id;
|
||||||
|
var functionName = nameof(GetProductPrice);
|
||||||
|
var parameters = new object[] { article };
|
||||||
|
|
||||||
|
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
|
||||||
|
{
|
||||||
|
return await databaseClient.GetProducts(article);
|
||||||
|
}) is not IEnumerable<Product> requestResult)
|
||||||
|
{
|
||||||
|
return "Загрузка...";
|
||||||
|
}
|
||||||
|
else if (!requestResult.Any())
|
||||||
|
{
|
||||||
|
return ExcelError.ExcelErrorNA;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var firstProduct = requestResult.First();
|
||||||
|
return Math.Round(firstProduct.Price * 1.2m, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFunction(Name = "РЕХАУЦЕНАРУБ")]
|
||||||
|
public static object GetProductPriceRub(string query, double dateField)
|
||||||
|
{
|
||||||
|
if (!ProductSku.TryParse(query, out var skus))
|
||||||
|
{
|
||||||
|
return ExcelError.ExcelErrorNA;
|
||||||
|
}
|
||||||
|
var article = skus.First().Id;
|
||||||
|
DateTime date = dateField == 0 ? DateTime.Today : DateTime.FromOADate(dateField);
|
||||||
|
|
||||||
|
|
||||||
|
var functionName = nameof(GetProductPriceRub);
|
||||||
|
var parameters = new object[] { date };
|
||||||
|
|
||||||
|
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
|
||||||
|
{
|
||||||
|
var requestResult = await currencyClient.GetExchangeRate(date);
|
||||||
|
return requestResult ?? -1m;
|
||||||
|
}) is not decimal exchangeRate)
|
||||||
|
{
|
||||||
|
return "Загрузка...";
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters = new object[] { query };
|
||||||
|
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
|
||||||
|
{
|
||||||
|
var products = await databaseClient.GetProducts(article);
|
||||||
|
var product = products.FirstOrDefault();
|
||||||
|
return product == null ? -1m :
|
||||||
|
product.Price * (decimal)exchangeRate * 1.2m;
|
||||||
|
}) is not decimal requestResult)
|
||||||
|
{
|
||||||
|
return "Загрузка...";
|
||||||
|
}
|
||||||
|
else if (requestResult < 0 || exchangeRate < 0)
|
||||||
|
{
|
||||||
|
return ExcelError.ExcelErrorNA;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Math.Round(requestResult, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,36 +1,39 @@
|
|||||||
using ExcelDna.Integration.CustomUI;
|
using ExcelDna.Integration.CustomUI;
|
||||||
|
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;
|
||||||
|
|
||||||
namespace RhSolutions.Controllers;
|
namespace RhSolutions.Controllers;
|
||||||
|
|
||||||
#if !NET472
|
|
||||||
[SupportedOSPlatform("windows")]
|
|
||||||
#endif
|
|
||||||
[ComVisible(true)]
|
[ComVisible(true)]
|
||||||
public class RibbonController : ExcelRibbon
|
public class RibbonController : ExcelRibbon
|
||||||
{
|
{
|
||||||
private static IRibbonUI ribbonUi;
|
private static IRibbonUI ribbonUi;
|
||||||
|
private static bool _workbookIsValid;
|
||||||
|
|
||||||
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' loadImage='LoadImage'>
|
||||||
<ribbon>
|
<ribbon>
|
||||||
<tabs>
|
<tabs>
|
||||||
<tab id='rau' label='RhSolutions'>
|
<tab id='rau' label='RhSolutions'>
|
||||||
<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' image='RhSolutions' 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'/>
|
||||||
<button id='merge' label='Объединить' size='normal' imageMso='Copy' onAction='OnToolPressed'/>
|
<button id='merge' label='Объединить' size='normal' imageMso='Copy' onAction='OnToolPressed'/>
|
||||||
<button id='dxfexport' getEnabled='GetDxfEnabled' label='Экспортировать в DXF' size='normal' imageMso='ExportExcel' onAction='OnToolPressed'/>
|
<button id='guess' getEnabled='GetGuessEnabled' label='Найти и экспортировать' size='large' imageMso='ControlWizards' onAction='OnToolPressed'/>
|
||||||
</group>
|
</group>
|
||||||
<group id='rausettings' getLabel='GetVersionLabel'>
|
<group id='fittingsCalc' label='Расчет фитингов'>
|
||||||
<button id='setPriceList' getLabel='GetPriceListPathLabel' size='large' imageMso='TableExcelSpreadsheetInsert' onAction='OnSetPricePressed'/>
|
<button id='fillsleeves' getEnabled='GetFittingsCalcEnabled' label='Гильзы' size='large' image='Sleeve' onAction='OnToolPressed'/>
|
||||||
|
<button id='fillcouplings' getEnabled='GetFittingsCalcEnabled' label='Муфты' size='large' image='Coupling' onAction='OnToolPressed'/>
|
||||||
|
</group>
|
||||||
|
<group id='exportTab' label='Экспорт'>
|
||||||
|
<button id='dxfexport' getEnabled='GetDxfEnabled' label='DXF' size='large' image='DXF' onAction='OnToolPressed'/>
|
||||||
|
</group>
|
||||||
|
<group id='settings' getLabel='GetVersionLabel'>
|
||||||
|
<button id='setPriceList' getLabel='GetPriceListPathLabel' size='large' image='RhSolutions' onAction='OnSetPricePressed'/>
|
||||||
</group>
|
</group>
|
||||||
</tab>
|
</tab>
|
||||||
</tabs>
|
</tabs>
|
||||||
@ -80,17 +83,10 @@ public class RibbonController : ExcelRibbon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GetConvertEnabled(IRibbonControl control)
|
public bool GetConvertEnabled(IRibbonControl control) => _workbookIsValid;
|
||||||
{
|
public bool GetDxfEnabled(IRibbonControl control) => _workbookIsValid;
|
||||||
if (RhSolutionsAddIn.Excel.ActiveWorkbook == null)
|
public bool GetFittingsCalcEnabled(IRibbonControl control) => _workbookIsValid;
|
||||||
return false;
|
public bool GetGuessEnabled(IRibbonControl control) => RhSolutionsAddIn.Excel.ActiveWorkbook != null && !_workbookIsValid;
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Worksheet worksheet = RhSolutionsAddIn.Excel.ActiveWorkbook.ActiveSheet;
|
|
||||||
return worksheet.IsValidSource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetExportEnabled(IRibbonControl control)
|
public bool GetExportEnabled(IRibbonControl control)
|
||||||
{
|
{
|
||||||
@ -104,18 +100,6 @@ public class RibbonController : ExcelRibbon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GetDxfEnabled(IRibbonControl control)
|
|
||||||
{
|
|
||||||
if (RhSolutionsAddIn.Excel.ActiveWorkbook == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Worksheet worksheet = RhSolutionsAddIn.Excel.ActiveWorkbook.ActiveSheet;
|
|
||||||
return worksheet.IsValidSource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetVersionLabel(IRibbonControl control)
|
public string GetVersionLabel(IRibbonControl control)
|
||||||
{
|
{
|
||||||
string version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
string version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||||
@ -125,6 +109,27 @@ public class RibbonController : ExcelRibbon
|
|||||||
public string GetPriceListPathLabel(IRibbonControl control)
|
public string GetPriceListPathLabel(IRibbonControl control)
|
||||||
{
|
{
|
||||||
string name = RhSolutionsAddIn.Configuration.GetPriceListFileName();
|
string name = RhSolutionsAddIn.Configuration.GetPriceListFileName();
|
||||||
return string.IsNullOrEmpty(name) ? "Шаблонный файл" : name;
|
return string.IsNullOrEmpty(name) ? "Указать шаблонный файл" : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateWorkbookValidation()
|
||||||
|
{
|
||||||
|
if (RhSolutionsAddIn.Excel.ActiveWorkbook == null)
|
||||||
|
_workbookIsValid = false;
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Worksheet worksheet = RhSolutionsAddIn.Excel.ActiveWorkbook.ActiveSheet;
|
||||||
|
_workbookIsValid = worksheet.IsValidSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnsurePriseListExists()
|
||||||
|
{
|
||||||
|
string pricelistPath = RhSolutionsAddIn.Configuration.GetPriceListPath();
|
||||||
|
if (!File.Exists(pricelistPath))
|
||||||
|
{
|
||||||
|
RhSolutionsAddIn.Configuration.SetPriceListPath(string.Empty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
RhSolutions.AddIn/Images/Coupling.png
Normal file
BIN
RhSolutions.AddIn/Images/Coupling.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
BIN
RhSolutions.AddIn/Images/DXF.png
Normal file
BIN
RhSolutions.AddIn/Images/DXF.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
RhSolutions.AddIn/Images/RhSolutions.png
Normal file
BIN
RhSolutions.AddIn/Images/RhSolutions.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 900 B |
BIN
RhSolutions.AddIn/Images/Sleeve.png
Normal file
BIN
RhSolutions.AddIn/Images/Sleeve.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||||||
// 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.6.2.1")]
|
[assembly: AssemblyVersion("1.9.5.1")]
|
||||||
[assembly: AssemblyFileVersion("1.6.2.1")]
|
[assembly: AssemblyFileVersion("1.9.5.1")]
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"Excel": {
|
"Excel": {
|
||||||
"commandName": "Executable",
|
"commandName": "Executable",
|
||||||
"executablePath": "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE",
|
"executablePath": "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE",
|
||||||
"commandLineArgs": "RhSolutions-AddIn64.xll"
|
"commandLineArgs": "/x \"RhSolutions-AddIn64.xll\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,11 @@
|
|||||||
<?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.Registration.dll" Pack="true" />
|
||||||
<Reference Path="Microsoft.Bcl.AsyncInterfaces.dll" Pack="true" />
|
<Reference Path="Microsoft.Bcl.AsyncInterfaces.dll" Pack="true" />
|
||||||
<Reference Path="Microsoft.Bcl.HashCode.dll" Pack="true" />
|
<Reference Path="Microsoft.Bcl.HashCode.dll" Pack="true" />
|
||||||
|
<Reference Path="Microsoft.Extensions.Caching.Abstractions.dll" Pack="true" />
|
||||||
|
<Reference Path="Microsoft.Extensions.Caching.Memory.dll" Pack="true" />
|
||||||
<Reference Path="Microsoft.Extensions.DependencyInjection.Abstractions.dll" Pack="true" />
|
<Reference Path="Microsoft.Extensions.DependencyInjection.Abstractions.dll" Pack="true" />
|
||||||
<Reference Path="Microsoft.Extensions.DependencyInjection.dll" Pack="true" />
|
<Reference Path="Microsoft.Extensions.DependencyInjection.dll" Pack="true" />
|
||||||
<Reference Path="Microsoft.Extensions.Http.dll" Pack="true" />
|
<Reference Path="Microsoft.Extensions.Http.dll" Pack="true" />
|
||||||
@ -19,5 +22,9 @@
|
|||||||
<Reference Path="System.Numerics.Vectors.dll" Pack="true" />
|
<Reference Path="System.Numerics.Vectors.dll" Pack="true" />
|
||||||
<Reference Path="System.Runtime.CompilerServices.Unsafe.dll" Pack="true" />
|
<Reference Path="System.Runtime.CompilerServices.Unsafe.dll" Pack="true" />
|
||||||
<Reference Path="System.Threading.Tasks.Extensions.dll" Pack="true" />
|
<Reference Path="System.Threading.Tasks.Extensions.dll" Pack="true" />
|
||||||
<Reference Path="System.ValueTuple.dll" Pack="true" />
|
<Reference Path="System.ValueTuple.dll" Pack="true" />
|
||||||
|
<Image Name='RhSolutions' Path='Images\RhSolutions.png' Pack='true' />
|
||||||
|
<Image Name='DXF' Path='Images\DXF.png' Pack='true' />
|
||||||
|
<Image Name='Sleeve' Path='Images\Sleeve.png' Pack='true' />
|
||||||
|
<Image Name='Coupling' Path='Images\Coupling.png' Pack='true' />
|
||||||
</DnaLibrary>
|
</DnaLibrary>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net472</TargetFrameworks>
|
<TargetFrameworks>net472;net6.0-windows</TargetFrameworks>
|
||||||
<LangVersion>10</LangVersion>
|
<LangVersion>10</LangVersion>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<RootNamespace>RhSolutions</RootNamespace>
|
<RootNamespace>RhSolutions</RootNamespace>
|
||||||
@ -9,35 +9,36 @@
|
|||||||
<UseWindowsForms>true</UseWindowsForms>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
|
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
|
||||||
<StartupObject />
|
|
||||||
</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.8.0" />
|
||||||
<TreatAsUsed>true</TreatAsUsed>
|
<PackageReference Include="ExcelDna.Integration" Version="1.8.0" />
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="ExcelDna.Integration" 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="ExcelDna.Registration" Version="1.8.0" />
|
||||||
|
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
|
<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="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
|
||||||
<PackageReference Include="netDxf" Version="2022.11.2" />
|
<PackageReference Include="netDxf" Version="2022.11.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="RhSolutions.ProductSku" Version="1.0.2" />
|
|
||||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
||||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\RhSolutions.ProductSku\RhSolutions.ProductSku.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Images\Coupling.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Images\DXF.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Images\RhSolutions.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Images\Sleeve.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -1,114 +1,43 @@
|
|||||||
using System.Configuration;
|
using Microsoft.Win32;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace RhSolutions.Services;
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
public class AddInConfiguration : ApplicationSettingsBase, IAddInConfiguration
|
public class AddInConfiguration : IAddInConfiguration
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, string> _priceListHeaders;
|
private RegistryKey _rootKey;
|
||||||
|
private string _priceListPath;
|
||||||
|
private Dictionary<string, string> _priceListHeaders;
|
||||||
|
|
||||||
|
public event IAddInConfiguration.SettingsHandler OnSettingsChange;
|
||||||
|
|
||||||
public AddInConfiguration()
|
public AddInConfiguration()
|
||||||
{
|
{
|
||||||
_priceListHeaders = new Dictionary<string, string>()
|
_rootKey = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\RhSolutions\RhSolutions-AddIn");
|
||||||
|
_priceListPath = (string)_rootKey.GetValue("PriceListPath");
|
||||||
|
_priceListHeaders = new()
|
||||||
{
|
{
|
||||||
["Amount"] = AmountHeader,
|
["Amount"] = "Кол-во",
|
||||||
["OldSku"] = OldSkuHeader,
|
["OldSku"] = "Прежний материал",
|
||||||
["Sku"] = SkuHeader,
|
["Sku"] = "Актуальный материал",
|
||||||
["ProductLine"] = ProductLineHeader,
|
["ProductLine"] = "Программа",
|
||||||
["Name"] = NameHeader,
|
["Name"] = "Наименование",
|
||||||
["Measure"] = MeasureHeader
|
["Measure"] = "Ед. изм."
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[UserScopedSetting]
|
public string GetPriceListFileName() => Path.GetFileName(_priceListPath);
|
||||||
[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]
|
|
||||||
[DefaultSettingValue("Ед. изм.")]
|
|
||||||
public string MeasureHeader
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return (string)this[nameof(MeasureHeader)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
public Dictionary<string, string> GetPriceListHeaders() => _priceListHeaders;
|
||||||
}
|
public string GetPriceListPath() => _priceListPath;
|
||||||
|
|
||||||
|
public void SaveSettings()
|
||||||
|
{
|
||||||
|
_rootKey.SetValue("PriceListPath", _priceListPath);
|
||||||
|
OnSettingsChange.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPriceListPath(string value)
|
||||||
|
{
|
||||||
|
_priceListPath = value;
|
||||||
|
}
|
||||||
|
}
|
67
RhSolutions.AddIn/Services/CouplingsCalculator.cs
Normal file
67
RhSolutions.AddIn/Services/CouplingsCalculator.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public class CouplingsCalculator : IFittingsCalculator
|
||||||
|
{
|
||||||
|
private static readonly string pattern =
|
||||||
|
@"(^|\W)труба.*(?'Diameter'16|20|25|32|40|50|63).*(отрезки|бухт[ае])\D*(?'Length'\d{1,3})(\D|$)";
|
||||||
|
|
||||||
|
public Dictionary<Product, double> Calculate(Dictionary<Product, double> products)
|
||||||
|
{
|
||||||
|
Dictionary<string, double> result = new()
|
||||||
|
{
|
||||||
|
["16"] = 0,
|
||||||
|
["20"] = 0,
|
||||||
|
["25"] = 0,
|
||||||
|
["32"] = 0,
|
||||||
|
["40"] = 0,
|
||||||
|
["50"] = 0,
|
||||||
|
["63"] = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
var rautitanProducts = products.Where(kvp => kvp.Key.ProductLines.Contains("RAUTITAN"));
|
||||||
|
Regex regex = new(pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
foreach (var kvp in rautitanProducts)
|
||||||
|
{
|
||||||
|
var match = regex.Match(kvp.Key.Name);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
string diameter = match.Groups["Diameter"].Value;
|
||||||
|
int packingLength = int.Parse(match.Groups["Length"].Value);
|
||||||
|
result[diameter] += GetCouplesCount(kvp.Value, packingLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
.ToDictionary(kvp =>
|
||||||
|
kvp.Key switch
|
||||||
|
{
|
||||||
|
"16" => new Product("11080111001"),
|
||||||
|
"20" => new Product("11080121001"),
|
||||||
|
"25" => new Product("11080131001"),
|
||||||
|
"32" => new Product("11080141001"),
|
||||||
|
"40" => new Product("11080151001"),
|
||||||
|
"50" => new Product("14563021001"),
|
||||||
|
"63" => new Product("14563031001"),
|
||||||
|
_ => throw new Exception($"Неизвестный диаметр {kvp.Key}")
|
||||||
|
}, kvp => kvp.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetCouplesCount(double amount, int packingLength)
|
||||||
|
{
|
||||||
|
if (amount < packingLength)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (amount % packingLength == 0)
|
||||||
|
{
|
||||||
|
return (int)amount / packingLength - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (int)amount / packingLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
RhSolutions.AddIn/Services/CurrencyClient.cs
Normal file
50
RhSolutions.AddIn/Services/CurrencyClient.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public class CurrencyClient : ICurrencyClient
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly IMemoryCache _memoryCache;
|
||||||
|
private const string _requestAddress = @"https://www.cbr.ru/scripts/XML_daily.asp?date_req=";
|
||||||
|
public HttpStatusCode StatusCode { get; private set; }
|
||||||
|
|
||||||
|
public CurrencyClient(HttpClient httpClient, IMemoryCache memoryCache)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_memoryCache = memoryCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<decimal?> GetExchangeRate(DateTime date)
|
||||||
|
{
|
||||||
|
if (!_memoryCache.TryGetValue(date, out decimal exchangeRate))
|
||||||
|
{
|
||||||
|
string request = $"{_requestAddress}{date.Date:dd/MM/yyyy}";
|
||||||
|
HttpResponseMessage response = await _httpClient.GetAsync(request);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
string xml = await response.Content.ReadAsStringAsync();
|
||||||
|
XElement valCourses = XElement.Parse(xml);
|
||||||
|
exchangeRate = decimal.Parse(valCourses.Elements("Valute")
|
||||||
|
.Where(e => e.Element("Name").Value == "Евро")
|
||||||
|
.FirstOrDefault()
|
||||||
|
.Element("Value").Value);
|
||||||
|
var cacheEntryOptions = new MemoryCacheEntryOptions()
|
||||||
|
.SetSlidingExpiration(TimeSpan.FromHours(1));
|
||||||
|
_memoryCache.Set(date, exchangeRate, cacheEntryOptions);
|
||||||
|
return exchangeRate;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
StatusCode = response.StatusCode;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exchangeRate;
|
||||||
|
}
|
||||||
|
}
|
19
RhSolutions.AddIn/Services/CurrentPriceWriter.cs
Normal file
19
RhSolutions.AddIn/Services/CurrentPriceWriter.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#if !NET472
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
using RhSolutions.Tools;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public class CurrentPriceWriter : ExcelWriterBase, IWriter, IDisposable
|
||||||
|
{
|
||||||
|
public CurrentPriceWriter(Application application, IAddInConfiguration configuration)
|
||||||
|
{
|
||||||
|
_application = application;
|
||||||
|
_resultBar = new();
|
||||||
|
_headers = configuration.GetPriceListHeaders();
|
||||||
|
_worksheet = _application.ActiveSheet;
|
||||||
|
_appendValues = false;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using Newtonsoft.Json;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -7,42 +8,87 @@ namespace RhSolutions.Services;
|
|||||||
|
|
||||||
public class DatabaseClient : IDatabaseClient
|
public class DatabaseClient : IDatabaseClient
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly IMemoryCache _memoryCache;
|
||||||
public HttpStatusCode StatusCode { get; private set; }
|
public HttpStatusCode StatusCode { get; private set; }
|
||||||
|
|
||||||
public DatabaseClient(IServiceProvider provider)
|
public DatabaseClient(HttpClient httpClient, IMemoryCache memoryCache)
|
||||||
{
|
{
|
||||||
this.serviceProvider = provider;
|
_httpClient = httpClient;
|
||||||
|
_memoryCache = memoryCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Product>> GetProducts(string line)
|
public async Task<IEnumerable<Product>> GetProducts(string line)
|
||||||
{
|
{
|
||||||
string request;
|
|
||||||
|
|
||||||
if (ProductSku.TryParse(line, out var skus))
|
if (ProductSku.TryParse(line, out var skus))
|
||||||
{
|
{
|
||||||
request = @"https://rh.cebotari.ru/api/products/" + skus.FirstOrDefault().ToString();
|
ProductSku sku = skus.FirstOrDefault();
|
||||||
|
string request = @"https://rh.cebotari.ru/api/products/" + sku.ToString();
|
||||||
|
|
||||||
|
if (!_memoryCache.TryGetValue(sku, out IEnumerable<Product> products))
|
||||||
|
{
|
||||||
|
var response = await _httpClient.GetAsync(request);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
string json = await response.Content.ReadAsStringAsync();
|
||||||
|
products = JsonConvert.DeserializeObject<IEnumerable<Product>>(json) ?? Enumerable.Empty<Product>();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
StatusCode = response.StatusCode;
|
||||||
|
return Enumerable.Empty<Product>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var cacheEntryOptions = new MemoryCacheEntryOptions()
|
||||||
|
.SetSlidingExpiration(TimeSpan.FromHours(1));
|
||||||
|
_memoryCache.Set(sku, products, cacheEntryOptions);
|
||||||
|
return products;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return products;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
request = @"https://rh.cebotari.ru/api/search?query=" + line;
|
UriBuilder builder = new(@"https://rh.cebotari.ru/api/search")
|
||||||
}
|
{
|
||||||
|
Query = $"query={line.Replace("&", "%26")}"
|
||||||
|
};
|
||||||
|
string request = builder.Uri.AbsoluteUri;
|
||||||
|
|
||||||
|
if (!_memoryCache.TryGetValue(line, out IEnumerable<Product> products))
|
||||||
|
{
|
||||||
|
var response = await _httpClient.GetAsync(request);
|
||||||
|
|
||||||
var client = serviceProvider.GetRequiredService<HttpClient>();
|
try
|
||||||
var response = await client.GetAsync(request);
|
{
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
string json = await response.Content.ReadAsStringAsync();
|
||||||
|
products = JsonConvert.DeserializeObject<IEnumerable<Product>>(json) ?? Enumerable.Empty<Product>();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
StatusCode = response.StatusCode;
|
||||||
|
return Enumerable.Empty<Product>();
|
||||||
|
}
|
||||||
|
|
||||||
try
|
var cacheEntryOptions = new MemoryCacheEntryOptions()
|
||||||
{
|
.SetSlidingExpiration(TimeSpan.FromHours(1));
|
||||||
response.EnsureSuccessStatusCode();
|
_memoryCache.Set(line, products, cacheEntryOptions);
|
||||||
string json = await response.Content.ReadAsStringAsync();
|
if (products.Any())
|
||||||
return JsonConvert.DeserializeObject<IEnumerable<Product>>(json) ?? Enumerable.Empty<Product>();
|
{
|
||||||
}
|
_memoryCache.Set(products.First(), products, cacheEntryOptions);
|
||||||
catch
|
}
|
||||||
{
|
return products;
|
||||||
StatusCode = response.StatusCode;
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
return Enumerable.Empty<Product>();
|
return products;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -60,7 +60,10 @@ public class ExcelReader : IReader, IDisposable
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Product product = new(currentSku);
|
Product product = new(currentSku)
|
||||||
|
{
|
||||||
|
Name = $"Распознанный артикул"
|
||||||
|
};
|
||||||
|
|
||||||
if (readResult.ContainsKey(product))
|
if (readResult.ContainsKey(product))
|
||||||
{
|
{
|
||||||
@ -76,7 +79,7 @@ public class ExcelReader : IReader, IDisposable
|
|||||||
return readResult;
|
return readResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<(string, Dictionary<Product, double>)>
|
public IEnumerable<(string, Dictionary<Product, double>)>
|
||||||
ReadProducts(IEnumerable<Worksheet> worksheets)
|
ReadProducts(IEnumerable<Worksheet> worksheets)
|
||||||
{
|
{
|
||||||
List<(string, Dictionary<Product, double>)> result = new();
|
List<(string, Dictionary<Product, double>)> result = new();
|
||||||
@ -90,27 +93,29 @@ public class ExcelReader : IReader, IDisposable
|
|||||||
string wbName = Path.GetFileNameWithoutExtension(
|
string wbName = Path.GetFileNameWithoutExtension(
|
||||||
worksheet.Parent.Name);
|
worksheet.Parent.Name);
|
||||||
|
|
||||||
Range AmountCell = worksheet.Cells.Find(headers["Amount"]),
|
Range amountCell = worksheet.Cells.Find(headers["Amount"]);
|
||||||
SkuCell = worksheet.Cells.Find(headers["Sku"]),
|
Range headerRow = amountCell.EntireRow;
|
||||||
ProductLineCell = worksheet.Cells.Find(headers["ProductLine"]),
|
Range skuCell = headerRow.Find(headers["Sku"]),
|
||||||
NameCell = worksheet.Cells.Find(headers["Name"]),
|
productLineCell = headerRow.Find(headers["ProductLine"]),
|
||||||
MeasureCell = worksheet.Cells.Find(headers["Measure"]);
|
nameCell = headerRow.Find(headers["Name"]),
|
||||||
var lastRowIndex = worksheet.Cells[worksheet.Rows.Count, AmountCell.Column]
|
measureCell = headerRow.Find(headers["Measure"]);
|
||||||
|
|
||||||
|
var lastRowIndex = worksheet.Cells[worksheet.Rows.Count, skuCell.Column]
|
||||||
.End[XlDirection.xlUp].Row;
|
.End[XlDirection.xlUp].Row;
|
||||||
|
|
||||||
Dictionary<Product, double> readResult = new();
|
Dictionary<Product, double> readResult = new();
|
||||||
|
|
||||||
for (int row = AmountCell.Row + 1; row <= lastRowIndex; row++)
|
for (int row = amountCell.Row + 1; row <= lastRowIndex; row++)
|
||||||
{
|
{
|
||||||
double? amount = worksheet.Cells[row, AmountCell.Column].Value2 as double?;
|
double? amount = worksheet.Cells[row, amountCell.Column].Value2 as double?;
|
||||||
|
|
||||||
if (amount != null && amount.Value != 0)
|
if (amount != null && amount.Value != 0)
|
||||||
{
|
{
|
||||||
object productLine = worksheet.Cells[row, ProductLineCell.Column].Value2;
|
object productLine = worksheet.Cells[row, productLineCell.Column].Value2;
|
||||||
object name = worksheet.Cells[row, NameCell.Column].Value2;
|
object name = worksheet.Cells[row, nameCell.Column].Value2;
|
||||||
object sku = worksheet.Cells[row, SkuCell.Column].Value2;
|
object sku = worksheet.Cells[row, skuCell.Column].Value2;
|
||||||
object measure = worksheet.Cells[row, MeasureCell.Column].Value2;
|
object measure = worksheet.Cells[row, measureCell.Column].Value2;
|
||||||
var productMeasure = measure.ToString() switch
|
var productMeasure = measure?.ToString() switch
|
||||||
{
|
{
|
||||||
"м" => Measure.M,
|
"м" => Measure.M,
|
||||||
"шт" => Measure.P,
|
"шт" => Measure.P,
|
||||||
@ -127,7 +132,7 @@ public class ExcelReader : IReader, IDisposable
|
|||||||
Product p = new(sku.ToString())
|
Product p = new(sku.ToString())
|
||||||
{
|
{
|
||||||
ProductLines = new List<string>() { productLine.ToString() },
|
ProductLines = new List<string>() { productLine.ToString() },
|
||||||
Name = name.ToString(),
|
Name = name.ToString().Split('\n').First(),
|
||||||
ProductMeasure = productMeasure
|
ProductMeasure = productMeasure
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -146,11 +151,15 @@ public class ExcelReader : IReader, IDisposable
|
|||||||
|
|
||||||
result.Add((wbName, readResult));
|
result.Add((wbName, readResult));
|
||||||
}
|
}
|
||||||
return result;
|
return result.OrderBy(x => x.Item1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<(string, Dictionary<Product, double>)> ReadProducts(string[] files)
|
public IEnumerable<(string, Dictionary<Product, double>)> ReadProducts(string[] files)
|
||||||
{
|
{
|
||||||
|
HashSet<string> openedFiles = RhSolutionsAddIn.Excel.Workbooks
|
||||||
|
.Cast<Workbook>()
|
||||||
|
.Select(wb => wb.FullName)
|
||||||
|
.ToHashSet();
|
||||||
_progressBar = new("Открываю исходные файлы...", files.Length);
|
_progressBar = new("Открываю исходные файлы...", files.Length);
|
||||||
List<Worksheet> worksheets = new();
|
List<Worksheet> worksheets = new();
|
||||||
|
|
||||||
@ -165,7 +174,11 @@ public class ExcelReader : IReader, IDisposable
|
|||||||
var result = ReadProducts(worksheets);
|
var result = ReadProducts(worksheets);
|
||||||
foreach (var ws in worksheets)
|
foreach (var ws in worksheets)
|
||||||
{
|
{
|
||||||
ws.Parent.Close();
|
string file = (string)ws.Parent.FullName;
|
||||||
|
if (!openedFiles.Contains(file))
|
||||||
|
{
|
||||||
|
ws.Parent.Close(SaveChanges: false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1,255 +0,0 @@
|
|||||||
#if !NET472
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
using RhSolutions.Tools;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
namespace RhSolutions.Services;
|
|
||||||
|
|
||||||
#if !NET472
|
|
||||||
[SupportedOSPlatform("windows")]
|
|
||||||
#endif
|
|
||||||
public class ExcelWriter : IWriter, 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 ExcelWriter(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.ToString(), positionAmount.Key.ProductLines.FirstOrDefault());
|
|
||||||
|
|
||||||
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.ToString(), positionAmount.Key.ProductLines.FirstOrDefault());
|
|
||||||
|
|
||||||
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.Article;
|
|
||||||
row = GetPositionRow(skuColumn, sku, positionAmount.Key.ProductLines.FirstOrDefault());
|
|
||||||
|
|
||||||
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.ProductLines.First();
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
267
RhSolutions.AddIn/Services/ExcelWriterBase.cs
Normal file
267
RhSolutions.AddIn/Services/ExcelWriterBase.cs
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace RhSolutions.Services
|
||||||
|
{
|
||||||
|
public abstract class ExcelWriterBase
|
||||||
|
{
|
||||||
|
protected Application _application;
|
||||||
|
protected Dictionary<string, string> _headers;
|
||||||
|
protected ResultBar _resultBar;
|
||||||
|
protected ProgressBar _progressBar;
|
||||||
|
|
||||||
|
protected Worksheet _worksheet;
|
||||||
|
protected Range _amountCell;
|
||||||
|
protected Range _nameCell;
|
||||||
|
protected Range _oldSkuCell;
|
||||||
|
protected Range _programLineCell;
|
||||||
|
protected Range _skuCell;
|
||||||
|
|
||||||
|
protected bool _appendValues = true;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_progressBar?.Dispose();
|
||||||
|
_resultBar?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteProducts(Dictionary<Product, double> products)
|
||||||
|
{
|
||||||
|
WriteProducts(new[] { (string.Empty, products) });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteProducts(IEnumerable<(string, Dictionary<Product, double>)> products)
|
||||||
|
{
|
||||||
|
if (!_worksheet.IsValidSource())
|
||||||
|
{
|
||||||
|
_application.ActiveWorkbook.Close();
|
||||||
|
throw new ArgumentException(
|
||||||
|
$"Целевой файл {_application.ActiveWorkbook.Name} не является прайс-листом.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowFilteredData();
|
||||||
|
|
||||||
|
_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)
|
||||||
|
{
|
||||||
|
FillAmounts(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.NumberFormat = "@";
|
||||||
|
newColumnHeader.Value2 = $"{product.Item1}";
|
||||||
|
newColumnHeader.WrapText = true;
|
||||||
|
|
||||||
|
foreach (var kvp in product.Item2)
|
||||||
|
{
|
||||||
|
FillAmounts(kvp, _amountCell.Column - 1, _amountCell.Column);
|
||||||
|
_progressBar.Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FilterByAmount();
|
||||||
|
_resultBar.Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteOutMissing(KeyValuePair<Product, double> productAmount, params int[] columns)
|
||||||
|
{
|
||||||
|
Range worksheetCells = _worksheet.Cells;
|
||||||
|
Range worksheetRows = _worksheet.Rows;
|
||||||
|
int skuColumn = _skuCell.Column;
|
||||||
|
int groupColumn = _programLineCell.Column;
|
||||||
|
int nameColumn = _nameCell.Column;
|
||||||
|
Product product = productAmount.Key;
|
||||||
|
|
||||||
|
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 = product.ProductLines.FirstOrDefault() ?? string.Empty;
|
||||||
|
worksheetCells[row, nameColumn].Value2 = $"{product.Name} (не найден арт. {product.ProductSku})";
|
||||||
|
worksheetCells[row, skuColumn].Value2 = "???";
|
||||||
|
if (_oldSkuCell != null)
|
||||||
|
{
|
||||||
|
worksheetCells[row, _oldSkuCell.Column].Value2 = product.ProductSku;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (int column in columns)
|
||||||
|
{
|
||||||
|
Range cell = worksheetCells[row, column];
|
||||||
|
cell.AddValue(productAmount.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillCells(double amount, int row, int[] columns)
|
||||||
|
{
|
||||||
|
foreach (int column in columns)
|
||||||
|
{
|
||||||
|
Range cell = _worksheet.Cells[row, column];
|
||||||
|
if (_appendValues)
|
||||||
|
{
|
||||||
|
cell.AddValue(amount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (amount == 0)
|
||||||
|
{
|
||||||
|
cell.Value2 = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cell.Value2 = amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillAmounts(KeyValuePair<Product, double> positionAmount, params int[] columns)
|
||||||
|
{
|
||||||
|
Range range = _skuCell.EntireColumn;
|
||||||
|
ProductSku sku = positionAmount.Key.ProductSku;
|
||||||
|
string productLine = positionAmount.Key.ProductLines.FirstOrDefault();
|
||||||
|
|
||||||
|
int? row = GetPositionRow(range, sku, productLine);
|
||||||
|
|
||||||
|
if (row != null)
|
||||||
|
{
|
||||||
|
FillCells(positionAmount.Value, row.Value, columns);
|
||||||
|
_resultBar.IncrementSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_oldSkuCell != null)
|
||||||
|
{
|
||||||
|
range = _oldSkuCell.EntireColumn;
|
||||||
|
row = GetPositionRow(range, sku, productLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row == null)
|
||||||
|
{
|
||||||
|
range = _skuCell.EntireColumn;
|
||||||
|
row = GetPositionRow(range, sku, productLine, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row != null)
|
||||||
|
{
|
||||||
|
Range nameCell = _worksheet.Cells[row, _nameCell.Column];
|
||||||
|
if (!Regex.IsMatch(nameCell.Value2, @"арт. \d{11}"))
|
||||||
|
{
|
||||||
|
nameCell.AddValue($"(замена арт. {sku})");
|
||||||
|
}
|
||||||
|
|
||||||
|
FillCells(positionAmount.Value, row.Value, columns);
|
||||||
|
_resultBar.IncrementReplaced();
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteOutMissing(positionAmount, columns);
|
||||||
|
_resultBar.IncrementNotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FilterByAmount()
|
||||||
|
{
|
||||||
|
if (_worksheet.AutoFilterMode)
|
||||||
|
{
|
||||||
|
AutoFilter filter = _worksheet.AutoFilter;
|
||||||
|
int startColumn = filter.Range.Column;
|
||||||
|
|
||||||
|
filter.Range.AutoFilter(_amountCell.Column - startColumn + 1, "<>0", XlAutoFilterOperator.xlAnd, "<>");
|
||||||
|
_worksheet.Range["A1"].Activate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowFilteredData()
|
||||||
|
{
|
||||||
|
if (_worksheet.AutoFilterMode)
|
||||||
|
{
|
||||||
|
AutoFilter filter = _worksheet.AutoFilter;
|
||||||
|
filter.ShowAllData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int? GetPositionRow(Range range, ProductSku sku, string productLine, bool justArticle = false)
|
||||||
|
{
|
||||||
|
string lookupString = justArticle ? sku.Article : sku.ToString();
|
||||||
|
Range found = range.Find(lookupString);
|
||||||
|
string foundGroupValue;
|
||||||
|
string foundSkuValue;
|
||||||
|
|
||||||
|
if (found == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int firstFoundRow = found.Row;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
foundSkuValue = _worksheet.Cells[found.Row, range.Column].Value2.ToString();
|
||||||
|
foundGroupValue = _worksheet.Cells[found.Row, _programLineCell.Column].Value2.ToString();
|
||||||
|
|
||||||
|
if (ProductSku.TryParse(foundSkuValue, out var skus))
|
||||||
|
{
|
||||||
|
if (skus.Any(s => s.Article == sku.Article) &&
|
||||||
|
(string.IsNullOrEmpty(productLine) || productLine.Equals(foundGroupValue)))
|
||||||
|
{
|
||||||
|
return found.Row;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
found = range.FindNext(found);
|
||||||
|
|
||||||
|
if (found.Row == firstFoundRow)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
found = range.FindNext(found);
|
||||||
|
|
||||||
|
if (found.Row == firstFoundRow)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
RhSolutions.AddIn/Services/FittingsCalculatorFactory.cs
Normal file
21
RhSolutions.AddIn/Services/FittingsCalculatorFactory.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public class FittingsCalculatorFactory
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public FittingsCalculatorFactory(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFittingsCalculator GetFittingsCalculator(string calculatorName)
|
||||||
|
{
|
||||||
|
return calculatorName switch
|
||||||
|
{
|
||||||
|
"Sleeves" => (IFittingsCalculator)_serviceProvider.GetService(typeof(SleevesCalculator)),
|
||||||
|
"Couplings" => (IFittingsCalculator)_serviceProvider.GetService(typeof(CouplingsCalculator)),
|
||||||
|
_ => throw new ArgumentException($"Незвестный интерфейс {nameof(IFittingsCalculator)}: {calculatorName}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
234
RhSolutions.AddIn/Services/GuessReader.cs
Normal file
234
RhSolutions.AddIn/Services/GuessReader.cs
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public class GuessReader : IReader
|
||||||
|
{
|
||||||
|
private readonly Application _application;
|
||||||
|
private ProgressBar _progressBar;
|
||||||
|
|
||||||
|
public GuessReader(Application application)
|
||||||
|
{
|
||||||
|
_application = application;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<Product, double> ReadProducts(Range range)
|
||||||
|
{
|
||||||
|
_progressBar = new("Ищу колонку с артикулами...", range.Columns.Count);
|
||||||
|
int? productColumnIndex = null;
|
||||||
|
|
||||||
|
for (int column = 1; column < range.Columns.Count + 1; column++)
|
||||||
|
{
|
||||||
|
_progressBar.Update();
|
||||||
|
if (IsProductColumn(range.Columns[column]))
|
||||||
|
{
|
||||||
|
productColumnIndex = column;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productColumnIndex == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Колонка с артикулом не определена");
|
||||||
|
}
|
||||||
|
|
||||||
|
int? amountColumnIndex = null;
|
||||||
|
int currentIndex = productColumnIndex.Value + 1;
|
||||||
|
_progressBar = new("Ищу колонку с количеством...", range.Columns.Count);
|
||||||
|
|
||||||
|
while (currentIndex > 0)
|
||||||
|
{
|
||||||
|
_progressBar.Update();
|
||||||
|
if (currentIndex > range.Columns.Count + 1)
|
||||||
|
{
|
||||||
|
currentIndex = productColumnIndex.Value - 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (currentIndex > productColumnIndex)
|
||||||
|
{
|
||||||
|
if (IsAmountColumn(range.Columns[currentIndex]))
|
||||||
|
{
|
||||||
|
amountColumnIndex = currentIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else currentIndex++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (IsAmountColumn(range.Columns[currentIndex]))
|
||||||
|
{
|
||||||
|
amountColumnIndex = currentIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else currentIndex--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amountColumnIndex == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Колонка с количеством не определена");
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetDictionaryFromColumns(range.Columns[productColumnIndex],
|
||||||
|
range.Columns[amountColumnIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsProductColumn(Range column)
|
||||||
|
{
|
||||||
|
int successCounter = 0;
|
||||||
|
var cells = column.Value2;
|
||||||
|
|
||||||
|
if (cells == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int row = 1; row < column.Rows.Count + 1; row++)
|
||||||
|
{
|
||||||
|
object currentCell = column.Rows.Count == 1 ? cells : cells[row, 1];
|
||||||
|
if (currentCell == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ProductSku.TryParse(currentCell.ToString(), out _))
|
||||||
|
{
|
||||||
|
successCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (successCounter > 2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return successCounter > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsAmountColumn(Range column)
|
||||||
|
{
|
||||||
|
int successCounter = 0;
|
||||||
|
var cells = column.Value2;
|
||||||
|
double maxValue = 30000;
|
||||||
|
|
||||||
|
if (cells == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column.Rows.Count == 1)
|
||||||
|
{
|
||||||
|
double? value = cells as double?;
|
||||||
|
|
||||||
|
return value != null
|
||||||
|
&& value != 0
|
||||||
|
&& value < maxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int row = 1; row < column.Rows.Count + 1; row++)
|
||||||
|
{
|
||||||
|
object currentCell = cells[row, 1];
|
||||||
|
double? value = currentCell as double?;
|
||||||
|
|
||||||
|
if (value == null
|
||||||
|
|| value == 0
|
||||||
|
|| value > maxValue)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++successCounter > 3)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return successCounter > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<Product, double> GetDictionaryFromColumns(Range productColumn, Range amountColumn)
|
||||||
|
{
|
||||||
|
Dictionary<Product, double> result = new();
|
||||||
|
var firstRowIndex = 1;
|
||||||
|
var lastRowIndex = amountColumn.Worksheet
|
||||||
|
.Cells[amountColumn.Worksheet.Rows.Count, amountColumn.Column]
|
||||||
|
.End[XlDirection.xlUp].Row;
|
||||||
|
_progressBar = new("Заполняю словарь значений...", lastRowIndex);
|
||||||
|
Worksheet worksheet = amountColumn.Worksheet;
|
||||||
|
|
||||||
|
for (int row = firstRowIndex; row < lastRowIndex + 1; row++)
|
||||||
|
{
|
||||||
|
_progressBar.Update();
|
||||||
|
object currentAmountCell = worksheet.Cells[row, amountColumn.Column].Value2;
|
||||||
|
object currentProductCell = worksheet.Cells[row, productColumn.Column].Value2;
|
||||||
|
double? amountValue = currentAmountCell as double?;
|
||||||
|
|
||||||
|
if (amountValue == null || amountValue == 0 || currentProductCell == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ProductSku.TryParse(currentProductCell.ToString(), out IEnumerable<ProductSku> skus))
|
||||||
|
{
|
||||||
|
Product p = new(skus.First())
|
||||||
|
{
|
||||||
|
Name = "Распознанный артикул"
|
||||||
|
};
|
||||||
|
if (result.ContainsKey(p))
|
||||||
|
{
|
||||||
|
result[p] += amountValue.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Add(p, amountValue.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<(string, Dictionary<Product, double>)> ReadProducts(IEnumerable<Worksheet> worksheets)
|
||||||
|
{
|
||||||
|
List<(string, Dictionary<Product, double>)> result = new();
|
||||||
|
foreach (Worksheet worksheet in worksheets)
|
||||||
|
{
|
||||||
|
string wbName = Path.GetFileNameWithoutExtension(
|
||||||
|
worksheet.Parent.Name);
|
||||||
|
Range range = worksheet.UsedRange;
|
||||||
|
var productsDict = ReadProducts(range);
|
||||||
|
result.Add((wbName, productsDict));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<(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();
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,12 @@
|
|||||||
using System.Configuration;
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
namespace RhSolutions.Services;
|
|
||||||
|
|
||||||
public interface IAddInConfiguration
|
public interface IAddInConfiguration
|
||||||
{
|
{
|
||||||
public string GetPriceListPath();
|
public string GetPriceListPath();
|
||||||
|
public void SetPriceListPath(string value);
|
||||||
public string GetPriceListFileName();
|
public string GetPriceListFileName();
|
||||||
public Dictionary<string, string> GetPriceListHeaders();
|
public Dictionary<string, string> GetPriceListHeaders();
|
||||||
public event SettingChangingEventHandler OnSettingsChange;
|
public delegate void SettingsHandler();
|
||||||
public void SetPriceListPath(string value);
|
public event SettingsHandler OnSettingsChange;
|
||||||
public void SaveSettings();
|
public void SaveSettings();
|
||||||
}
|
}
|
9
RhSolutions.AddIn/Services/ICurrencyClient.cs
Normal file
9
RhSolutions.AddIn/Services/ICurrencyClient.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public interface ICurrencyClient
|
||||||
|
{
|
||||||
|
public Task<decimal?> GetExchangeRate(DateTime date);
|
||||||
|
}
|
6
RhSolutions.AddIn/Services/IFittingsCalculator.cs
Normal file
6
RhSolutions.AddIn/Services/IFittingsCalculator.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public interface IFittingsCalculator
|
||||||
|
{
|
||||||
|
public Dictionary<Product, double> Calculate(Dictionary<Product, double> products);
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
public interface IReader : IDisposable
|
public interface IReader : IDisposable
|
||||||
{
|
{
|
||||||
public Dictionary<Product, double> ReadProducts(Range range);
|
public Dictionary<Product, double> ReadProducts(Range range);
|
||||||
public List<(string, Dictionary<Product, double>)> ReadProducts(IEnumerable<Worksheet> worksheets);
|
public IEnumerable<(string, Dictionary<Product, double>)> ReadProducts(IEnumerable<Worksheet> worksheets);
|
||||||
public List<(string, Dictionary<Product, double>)> ReadProducts(string[] files);
|
public IEnumerable<(string, Dictionary<Product, double>)> ReadProducts(string[] files);
|
||||||
new void Dispose();
|
new void Dispose();
|
||||||
}
|
}
|
||||||
|
34
RhSolutions.AddIn/Services/NewPriceWriter.cs
Normal file
34
RhSolutions.AddIn/Services/NewPriceWriter.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#if !NET472
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
using RhSolutions.Tools;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
#if !NET472
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
#endif
|
||||||
|
public class NewPriceWriter : ExcelWriterBase, IWriter, IDisposable
|
||||||
|
{
|
||||||
|
public NewPriceWriter(Application application, IAddInConfiguration configuration)
|
||||||
|
{
|
||||||
|
_application = application;
|
||||||
|
_resultBar = new();
|
||||||
|
_headers = configuration.GetPriceListHeaders();
|
||||||
|
_worksheet = OpenNewPrice(configuration.GetPriceListPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Worksheet OpenNewPrice(string pricelistPath)
|
||||||
|
{
|
||||||
|
if (_application.Workbooks
|
||||||
|
.Cast<Workbook>()
|
||||||
|
.FirstOrDefault(w => w.FullName == pricelistPath) != null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Шаблонный файл редактируется в другом месте");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _application.Workbooks.Open(pricelistPath, null, true).ActiveSheet;
|
||||||
|
}
|
||||||
|
}
|
21
RhSolutions.AddIn/Services/ReaderFactory.cs
Normal file
21
RhSolutions.AddIn/Services/ReaderFactory.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public class ReaderFactory
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public ReaderFactory(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReader GetReader(string readerName)
|
||||||
|
{
|
||||||
|
return readerName switch
|
||||||
|
{
|
||||||
|
"Guess" => (IReader)_serviceProvider.GetService(typeof(GuessReader)),
|
||||||
|
"Excel" => (IReader)_serviceProvider.GetService(typeof(ExcelReader)),
|
||||||
|
_ => throw new ArgumentException($"Незвестный интерфейс {nameof(IReader)}: {readerName}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
87
RhSolutions.AddIn/Services/SleevesCalculator.cs
Normal file
87
RhSolutions.AddIn/Services/SleevesCalculator.cs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace RhSolutions.Services;
|
||||||
|
|
||||||
|
public class SleevesCalculator : IFittingsCalculator
|
||||||
|
{
|
||||||
|
private const string doublePattern =
|
||||||
|
@"((?i)равнопроходная|угольник\s+90|угольник\s+45|Т-образная|Комплект\s+трубок(?i))(.+?\b(?<Sleeve>16|20|25|32|40|50|63)\b)+";
|
||||||
|
private const string singlePattern =
|
||||||
|
@"((?i)муфта|тройник|переходник|угольник|штуцер|Г-образная|заглушка(?i))(.+?\b(?<Sleeve>16|20|25|32|40|50|63)\b)+";
|
||||||
|
|
||||||
|
public Dictionary<Product, double> Calculate(Dictionary<Product, double> products)
|
||||||
|
{
|
||||||
|
Dictionary<string, double> result = new()
|
||||||
|
{
|
||||||
|
["16"] = 0,
|
||||||
|
["20"] = 0,
|
||||||
|
["25"] = 0,
|
||||||
|
["32"] = 0,
|
||||||
|
["40"] = 0,
|
||||||
|
["50"] = 0,
|
||||||
|
["63"] = 0,
|
||||||
|
["16PX"] = 0,
|
||||||
|
["20PX"] = 0,
|
||||||
|
["25PX"] = 0,
|
||||||
|
["32PX"] = 0,
|
||||||
|
["40PX"] = 0
|
||||||
|
};
|
||||||
|
var rautitanProducts = products.Where(kvp => kvp.Key.ProductLines.Contains("RAUTITAN"));
|
||||||
|
|
||||||
|
foreach (var kvp in rautitanProducts)
|
||||||
|
{
|
||||||
|
var doubleCollection = Regex.Matches(kvp.Key.Name, doublePattern);
|
||||||
|
if (doubleCollection.Count != 0)
|
||||||
|
{
|
||||||
|
CaptureCollection collection = doubleCollection[0].Groups["Sleeve"].Captures;
|
||||||
|
foreach (Capture sleeve in collection)
|
||||||
|
{
|
||||||
|
if (kvp.Key.Name.Contains("PX"))
|
||||||
|
{
|
||||||
|
result[$"{sleeve.Value}PX"] += kvp.Value * 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result[sleeve.Value] += kvp.Value * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var singleCollection = Regex.Matches(kvp.Key.Name, singlePattern);
|
||||||
|
if (singleCollection.Count != 0)
|
||||||
|
{
|
||||||
|
CaptureCollection collection = singleCollection[0].Groups["Sleeve"].Captures;
|
||||||
|
foreach (Capture sleeve in collection)
|
||||||
|
{
|
||||||
|
if (kvp.Key.Name.Contains("PX"))
|
||||||
|
{
|
||||||
|
result[$"{sleeve.Value}PX"] += kvp.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result[sleeve.Value] += kvp.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
.ToDictionary(kvp =>
|
||||||
|
kvp.Key switch
|
||||||
|
{
|
||||||
|
"16" => new Product("11080011001"),
|
||||||
|
"20" => new Product("11080021001"),
|
||||||
|
"25" => new Product("11080031001"),
|
||||||
|
"32" => new Product("11080041001"),
|
||||||
|
"40" => new Product("11080051001"),
|
||||||
|
"50" => new Product("11397713002"),
|
||||||
|
"63" => new Product("11397813002"),
|
||||||
|
"16PX" => new Product("11600011001"),
|
||||||
|
"20PX" => new Product("11600021001"),
|
||||||
|
"25PX" => new Product("11600031001"),
|
||||||
|
"32PX" => new Product("11600041001"),
|
||||||
|
"40PX" => new Product("11600051001"),
|
||||||
|
_ => throw new Exception($"Неизвестный диаметр {kvp.Key}")
|
||||||
|
}, kvp => kvp.Value);
|
||||||
|
}
|
||||||
|
}
|
@ -11,14 +11,12 @@ public class WriterFactory
|
|||||||
|
|
||||||
public IWriter GetWriter(string writerName)
|
public IWriter GetWriter(string writerName)
|
||||||
{
|
{
|
||||||
if (writerName.Equals("Dxf"))
|
return writerName switch
|
||||||
{
|
{
|
||||||
return (IWriter)_serviceProvider.GetService(typeof(DxfWriter));
|
"NewPrice" => (IWriter)_serviceProvider.GetService(typeof(NewPriceWriter)),
|
||||||
}
|
"CurrentPrice" => (IWriter)_serviceProvider.GetService(typeof(CurrentPriceWriter)),
|
||||||
|
"Dxf" => (IWriter)_serviceProvider.GetService(typeof(DxfWriter)),
|
||||||
else
|
_ => throw new ArgumentException($"Незвестный интерфейс {nameof(IWriter)}: {writerName}")
|
||||||
{
|
};
|
||||||
return (IWriter)_serviceProvider.GetService(typeof(ExcelWriter));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,7 +10,7 @@ namespace RhSolutions.Tools;
|
|||||||
#endif
|
#endif
|
||||||
internal class ConvertTool : Tool
|
internal class ConvertTool : Tool
|
||||||
{
|
{
|
||||||
public ConvertTool(IServiceProvider provider) : base(provider)
|
public ConvertTool(ReaderFactory readerFactory, WriterFactory writerFactory) : base(readerFactory, writerFactory)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,8 +18,9 @@ internal class ConvertTool : Tool
|
|||||||
{
|
{
|
||||||
Application app = RhSolutionsAddIn.Excel.Application;
|
Application app = RhSolutionsAddIn.Excel.Application;
|
||||||
Worksheet worksheet = app.ActiveWorkbook.ActiveSheet;
|
Worksheet worksheet = app.ActiveWorkbook.ActiveSheet;
|
||||||
|
_reader = _readerFactory.GetReader("Excel");
|
||||||
var products = _reader.ReadProducts(new[] { worksheet });
|
var products = _reader.ReadProducts(new[] { worksheet });
|
||||||
_writer = _writerFactory.GetWriter("Excel");
|
_writer = _writerFactory.GetWriter("NewPrice");
|
||||||
_writer.WriteProducts(products);
|
_writer.WriteProducts(products);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ namespace RhSolutions.Tools;
|
|||||||
#endif
|
#endif
|
||||||
internal class DxfTool : Tool
|
internal class DxfTool : Tool
|
||||||
{
|
{
|
||||||
public DxfTool(IServiceProvider provider) : base(provider)
|
public DxfTool(ReaderFactory readerFactory, WriterFactory writerFactory) : base(readerFactory, writerFactory)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,6 +17,7 @@ internal class DxfTool : Tool
|
|||||||
{
|
{
|
||||||
Application app = RhSolutionsAddIn.Excel.Application;
|
Application app = RhSolutionsAddIn.Excel.Application;
|
||||||
Worksheet worksheet = app.ActiveWorkbook.ActiveSheet;
|
Worksheet worksheet = app.ActiveWorkbook.ActiveSheet;
|
||||||
|
_reader = _readerFactory.GetReader("Excel");
|
||||||
var products = _reader.ReadProducts(new[] { worksheet });
|
var products = _reader.ReadProducts(new[] { worksheet });
|
||||||
_writer = _writerFactory.GetWriter("Dxf");
|
_writer = _writerFactory.GetWriter("Dxf");
|
||||||
_writer.WriteProducts(products);
|
_writer.WriteProducts(products);
|
||||||
|
@ -1,56 +1,43 @@
|
|||||||
using Microsoft.Office.Interop.Excel;
|
using RhSolutions.Controllers;
|
||||||
using RhSolutions.AddIn;
|
|
||||||
using RhSolutions.Controllers;
|
|
||||||
using System.Configuration;
|
|
||||||
#if !NET472
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace RhSolutions.Tools
|
namespace RhSolutions.Tools;
|
||||||
|
|
||||||
|
internal static class EventsUtil
|
||||||
{
|
{
|
||||||
#if !NET472
|
public static void Initialize()
|
||||||
[SupportedOSPlatform("windows")]
|
|
||||||
#endif
|
|
||||||
internal static class EventsUtil
|
|
||||||
{
|
{
|
||||||
public static void Initialize()
|
RibbonController.EnsurePriseListExists();
|
||||||
{
|
RhSolutionsAddIn.Excel.SheetSelectionChange += RefreshExportButton;
|
||||||
RhSolutionsAddIn.Excel.SheetSelectionChange += RefreshExportButton;
|
RhSolutionsAddIn.Excel.SheetActivate += RefreshButtons;
|
||||||
RhSolutionsAddIn.Excel.SheetActivate += RefreshConvertButton;
|
RhSolutionsAddIn.Excel.WorkbookActivate += RefreshButtons;
|
||||||
RhSolutionsAddIn.Excel.SheetActivate += RefreshDxfButton;
|
RhSolutionsAddIn.Configuration.OnSettingsChange += RefreshSettingTitle;
|
||||||
RhSolutionsAddIn.Excel.WorkbookActivate += RefreshConvertButton;
|
}
|
||||||
RhSolutionsAddIn.Excel.WorkbookActivate += RefreshDxfButton;
|
|
||||||
RhSolutionsAddIn.Configuration.OnSettingsChange += RefreshSettingTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Uninitialize()
|
public static void Uninitialize()
|
||||||
{
|
{
|
||||||
RhSolutionsAddIn.Excel.SheetSelectionChange -= RefreshExportButton;
|
RhSolutionsAddIn.Excel.SheetSelectionChange -= RefreshExportButton;
|
||||||
RhSolutionsAddIn.Excel.SheetActivate -= RefreshConvertButton;
|
RhSolutionsAddIn.Excel.SheetActivate -= RefreshButtons;
|
||||||
RhSolutionsAddIn.Excel.SheetActivate -= RefreshDxfButton;
|
RhSolutionsAddIn.Excel.WorkbookActivate -= RefreshButtons;
|
||||||
RhSolutionsAddIn.Excel.WorkbookActivate -= RefreshConvertButton;
|
RhSolutionsAddIn.Configuration.OnSettingsChange -= RefreshSettingTitle;
|
||||||
RhSolutionsAddIn.Excel.WorkbookActivate -= RefreshDxfButton;
|
}
|
||||||
RhSolutionsAddIn.Configuration.OnSettingsChange -= RefreshSettingTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RefreshConvertButton(object sh)
|
private static void RefreshButtons(object sh)
|
||||||
{
|
{
|
||||||
RibbonController.RefreshControl("convert");
|
RibbonController.UpdateWorkbookValidation();
|
||||||
}
|
RibbonController.RefreshControl("convert");
|
||||||
|
RibbonController.RefreshControl("dxfexport");
|
||||||
|
RibbonController.RefreshControl("guess");
|
||||||
|
RibbonController.RefreshControl("fillsleeves");
|
||||||
|
RibbonController.RefreshControl("fillcouplings");
|
||||||
|
}
|
||||||
|
|
||||||
private static void RefreshDxfButton(object sh)
|
private static void RefreshExportButton(object sh, Range target)
|
||||||
{
|
{
|
||||||
RibbonController.RefreshControl("dxfexport");
|
RibbonController.RefreshControl("export");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RefreshExportButton(object sh, Range target)
|
private static void RefreshSettingTitle()
|
||||||
{
|
{
|
||||||
RibbonController.RefreshControl("export");
|
RibbonController.RefreshControl("setPriceList");
|
||||||
}
|
|
||||||
|
|
||||||
private static void RefreshSettingTitle(object sender, SettingChangingEventArgs e)
|
|
||||||
{
|
|
||||||
RibbonController.RefreshControl("setPriceList");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using RhSolutions.AddIn;
|
#if !NET472
|
||||||
#if !NET472
|
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -10,15 +9,16 @@ namespace RhSolutions.Tools;
|
|||||||
#endif
|
#endif
|
||||||
internal class ExportTool : Tool
|
internal class ExportTool : Tool
|
||||||
{
|
{
|
||||||
public ExportTool(IServiceProvider provider) : base(provider)
|
public ExportTool(ReaderFactory readerFactory, WriterFactory writerFactory) : base(readerFactory, writerFactory)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Execute()
|
public override void Execute()
|
||||||
{
|
{
|
||||||
Application app = RhSolutionsAddIn.Excel.Application;
|
Application app = RhSolutionsAddIn.Excel.Application;
|
||||||
|
_reader = _readerFactory.GetReader("Excel");
|
||||||
var products = _reader.ReadProducts(app.Selection);
|
var products = _reader.ReadProducts(app.Selection);
|
||||||
_writer = _writerFactory.GetWriter("Excel");
|
_writer = _writerFactory.GetWriter("NewPrice");
|
||||||
_writer.WriteProducts(products);
|
_writer.WriteProducts(products);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
RhSolutions.AddIn/Tools/FittingsTool.cs
Normal file
25
RhSolutions.AddIn/Tools/FittingsTool.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
namespace RhSolutions.Tools;
|
||||||
|
|
||||||
|
internal class FittingsTool : Tool
|
||||||
|
{
|
||||||
|
private readonly FittingsCalculatorFactory _factory;
|
||||||
|
private string _calculatorName;
|
||||||
|
|
||||||
|
public FittingsTool(ReaderFactory readerFactory, WriterFactory writerFactory, FittingsCalculatorFactory calculatorFactory, string calculatorName) : base(readerFactory, writerFactory)
|
||||||
|
{
|
||||||
|
_factory = calculatorFactory;
|
||||||
|
_calculatorName = calculatorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Execute()
|
||||||
|
{
|
||||||
|
Application app = RhSolutionsAddIn.Excel.Application;
|
||||||
|
Worksheet worksheet = app.ActiveWorkbook.ActiveSheet;
|
||||||
|
_reader = _readerFactory.GetReader("Excel");
|
||||||
|
var products = _reader.ReadProducts(new[] { worksheet });
|
||||||
|
var calculator = _factory.GetFittingsCalculator(_calculatorName);
|
||||||
|
var fittings = calculator.Calculate(products.Select(p => p.Item2).First());
|
||||||
|
_writer = _writerFactory.GetWriter("CurrentPrice");
|
||||||
|
_writer.WriteProducts(fittings);
|
||||||
|
}
|
||||||
|
}
|
23
RhSolutions.AddIn/Tools/GuessTool.cs
Normal file
23
RhSolutions.AddIn/Tools/GuessTool.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#if !NET472
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace RhSolutions.Tools;
|
||||||
|
|
||||||
|
internal class GuessTool : Tool
|
||||||
|
{
|
||||||
|
public GuessTool(ReaderFactory readerFactory, WriterFactory writerFactory) : base(readerFactory, writerFactory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Execute()
|
||||||
|
{
|
||||||
|
Application app = RhSolutionsAddIn.Excel.Application;
|
||||||
|
Worksheet worksheet = app.ActiveWorkbook.ActiveSheet;
|
||||||
|
_reader = _readerFactory.GetReader("Guess");
|
||||||
|
var products = _reader.ReadProducts(new[] { worksheet });
|
||||||
|
_writer = _writerFactory.GetWriter("NewPrice");
|
||||||
|
_writer.WriteProducts(products);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ namespace RhSolutions.Tools;
|
|||||||
#endif
|
#endif
|
||||||
internal class MergeTool : Tool
|
internal class MergeTool : Tool
|
||||||
{
|
{
|
||||||
public MergeTool(IServiceProvider provider) : base(provider)
|
public MergeTool(ReaderFactory readerFactory, WriterFactory writerFactory) : base(readerFactory, writerFactory)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,8 +17,12 @@ internal class MergeTool : Tool
|
|||||||
{
|
{
|
||||||
IFileDialog dialog = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IFileDialog>();
|
IFileDialog dialog = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IFileDialog>();
|
||||||
string[] files = dialog.GetFiles();
|
string[] files = dialog.GetFiles();
|
||||||
var products = _reader.ReadProducts(files);
|
if (files.Length > 0)
|
||||||
_writer = _writerFactory.GetWriter("Excel");
|
{
|
||||||
_writer.WriteProducts(products);
|
_reader = _readerFactory.GetReader("Excel");
|
||||||
|
var products = _reader.ReadProducts(files);
|
||||||
|
_writer = _writerFactory.GetWriter("NewPrice");
|
||||||
|
_writer.WriteProducts(products);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ namespace RhSolutions.Tools;
|
|||||||
#if !NET472
|
#if !NET472
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
#endif
|
#endif
|
||||||
internal class ProgressBar : StatusbarBase
|
public class ProgressBar : StatusbarBase
|
||||||
{
|
{
|
||||||
private double CurrentProgress { get; set; }
|
private double CurrentProgress { get; set; }
|
||||||
private readonly double TaskWeight;
|
private readonly double TaskWeight;
|
||||||
|
@ -8,7 +8,7 @@ namespace RhSolutions.Tools;
|
|||||||
#if !NET472
|
#if !NET472
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
#endif
|
#endif
|
||||||
internal class ResultBar : StatusbarBase
|
public class ResultBar : StatusbarBase
|
||||||
{
|
{
|
||||||
private int Success { get; set; }
|
private int Success { get; set; }
|
||||||
private int Replaced { get; set; }
|
private int Replaced { get; set; }
|
||||||
|
@ -11,10 +11,16 @@ namespace RhSolutions.Tools;
|
|||||||
#if !NET472
|
#if !NET472
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
#endif
|
#endif
|
||||||
internal abstract class StatusbarBase : IDisposable
|
public abstract class StatusbarBase : IDisposable
|
||||||
{
|
{
|
||||||
protected Application Excel = RhSolutionsAddIn.Excel;
|
protected Application Excel = RhSolutionsAddIn.Excel;
|
||||||
|
|
||||||
|
[ExcelFunction(IsHidden = true)]
|
||||||
|
public static void StatusBarReset()
|
||||||
|
{
|
||||||
|
RhSolutionsAddIn.Excel.StatusBar = false;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void Update();
|
public abstract void Update();
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -9,20 +9,21 @@ namespace RhSolutions.Tools;
|
|||||||
#endif
|
#endif
|
||||||
internal abstract class Tool : IDisposable
|
internal abstract class Tool : IDisposable
|
||||||
{
|
{
|
||||||
protected readonly IReader _reader;
|
protected readonly ReaderFactory _readerFactory;
|
||||||
protected readonly WriterFactory _writerFactory;
|
protected readonly WriterFactory _writerFactory;
|
||||||
|
protected IReader _reader;
|
||||||
protected IWriter _writer;
|
protected IWriter _writer;
|
||||||
|
|
||||||
public Tool(IServiceProvider provider)
|
public Tool(ReaderFactory readerFactory, WriterFactory writerFactory)
|
||||||
{
|
{
|
||||||
_reader = provider.GetRequiredService<IReader>();
|
_readerFactory = readerFactory;
|
||||||
_writerFactory = provider.GetRequiredService<WriterFactory>();
|
_writerFactory = writerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_reader.Dispose();
|
_reader?.Dispose();
|
||||||
_writer.Dispose();
|
_writer?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void Execute();
|
public abstract void Execute();
|
||||||
|
@ -2,15 +2,22 @@
|
|||||||
|
|
||||||
internal class ToolFactory
|
internal class ToolFactory
|
||||||
{
|
{
|
||||||
|
static ReaderFactory readerFactory = RhSolutionsAddIn.ServiceProvider.GetService<ReaderFactory>();
|
||||||
|
static WriterFactory writerFactory = RhSolutionsAddIn.ServiceProvider.GetService<WriterFactory>();
|
||||||
|
static FittingsCalculatorFactory fittingsCalculatorFactory = RhSolutionsAddIn.ServiceProvider.GetService<FittingsCalculatorFactory>();
|
||||||
|
|
||||||
public Tool GetTool(string toolName)
|
public Tool GetTool(string toolName)
|
||||||
{
|
{
|
||||||
Tool tool = toolName switch
|
Tool tool = toolName switch
|
||||||
{
|
{
|
||||||
"export" => new ExportTool(RhSolutionsAddIn.ServiceProvider),
|
"export" => new ExportTool(readerFactory, writerFactory),
|
||||||
"convert" => new ConvertTool(RhSolutionsAddIn.ServiceProvider),
|
"convert" => new ConvertTool(readerFactory, writerFactory),
|
||||||
"merge" => new MergeTool(RhSolutionsAddIn.ServiceProvider),
|
"merge" => new MergeTool(readerFactory, writerFactory),
|
||||||
"dxfexport" => new DxfTool(RhSolutionsAddIn.ServiceProvider),
|
"dxfexport" => new DxfTool(readerFactory, writerFactory),
|
||||||
_ => throw new Exception("Неизвестный инструмент"),
|
"guess" => new GuessTool(readerFactory, writerFactory),
|
||||||
|
"fillsleeves" => new FittingsTool(readerFactory, writerFactory, fittingsCalculatorFactory, "Sleeves"),
|
||||||
|
"fillcouplings" => new FittingsTool(readerFactory, writerFactory, fittingsCalculatorFactory, "Couplings"),
|
||||||
|
_ => throw new Exception($"Неизвестный инструмент {toolName}"),
|
||||||
};
|
};
|
||||||
return tool;
|
return tool;
|
||||||
}
|
}
|
||||||
|
@ -14,22 +14,32 @@ public static class WorksheetExtensions
|
|||||||
|
|
||||||
public static bool IsValidSource(this Worksheet worksheet)
|
public static bool IsValidSource(this Worksheet worksheet)
|
||||||
{
|
{
|
||||||
Range amountCell;
|
Range headerRow;
|
||||||
Range skuCell;
|
|
||||||
Range programLineCell;
|
|
||||||
Range nameCell;
|
|
||||||
Range measureCell;
|
|
||||||
|
|
||||||
Range[] cells = new[]
|
string[] fields = pricelistParameters.Values
|
||||||
|
.Where(v => v != "Прежний материал")
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var value = worksheet.Cells.Find(fields[0]);
|
||||||
|
|
||||||
|
if (value == null)
|
||||||
{
|
{
|
||||||
amountCell = worksheet.Cells.Find(pricelistParameters["Amount"]),
|
return false;
|
||||||
skuCell = worksheet.Cells.Find(pricelistParameters["Sku"]),
|
}
|
||||||
programLineCell = worksheet.Cells.Find(pricelistParameters["ProductLine"]),
|
else
|
||||||
nameCell = worksheet.Cells.Find(pricelistParameters["Name"]),
|
{
|
||||||
measureCell = worksheet.Cells.Find(pricelistParameters["Measure"])
|
headerRow = value.EntireRow;
|
||||||
};
|
}
|
||||||
|
|
||||||
return cells.All(x => x != null);
|
for (int i = 1; i < fields.Length; i++)
|
||||||
|
{
|
||||||
|
if (headerRow.Find(fields[i]) == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddValue(this Range range, double value)
|
public static void AddValue(this Range range, double value)
|
||||||
@ -44,5 +54,18 @@ public static class WorksheetExtensions
|
|||||||
range.Value2 += value;
|
range.Value2 += value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void AddValue(this Range range, string value)
|
||||||
|
{
|
||||||
|
if (range.Value2 == null)
|
||||||
|
{
|
||||||
|
range.Value2 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
range.Value2 = $"{range.Value2} {value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
3
RhSolutions.ProductSku/Measure.cs
Normal file
3
RhSolutions.ProductSku/Measure.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
namespace RhSolutions.Models;
|
||||||
|
|
||||||
|
public enum Measure { Kg, M, M2, P }
|
91
RhSolutions.ProductSku/Product.cs
Normal file
91
RhSolutions.ProductSku/Product.cs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace RhSolutions.Models;
|
||||||
|
public class Product : IDisposable
|
||||||
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public string Id
|
||||||
|
{
|
||||||
|
get => ProductSku.Id;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
ProductSku = new(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public ProductSku ProductSku { get; set; }
|
||||||
|
public List<ProductSku> DeprecatedSkus { get; set; } = new();
|
||||||
|
public List<string> ProductLines { get; set; } = new();
|
||||||
|
public bool IsOnWarehouse { get; set; } = false;
|
||||||
|
public Measure ProductMeasure { get; set; }
|
||||||
|
public double? DeliveryMakeUp { get; set; }
|
||||||
|
public decimal Price { get; set; }
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
public Product(string id,
|
||||||
|
string name,
|
||||||
|
ProductSku productSku,
|
||||||
|
ProductSku[] deprecatedSkus,
|
||||||
|
string[] productLines,
|
||||||
|
bool isOnWarehouse,
|
||||||
|
int productMeasure,
|
||||||
|
int deliveryMakeUp,
|
||||||
|
decimal price)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Name = name;
|
||||||
|
ProductSku = productSku;
|
||||||
|
DeprecatedSkus = deprecatedSkus.ToList();
|
||||||
|
ProductLines = productLines.ToList();
|
||||||
|
IsOnWarehouse = isOnWarehouse;
|
||||||
|
ProductMeasure = (Measure)productMeasure;
|
||||||
|
DeliveryMakeUp = deliveryMakeUp;
|
||||||
|
Price = price;
|
||||||
|
}
|
||||||
|
public Product(string sku)
|
||||||
|
{
|
||||||
|
ProductSku = new(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Product(ProductSku productSku)
|
||||||
|
{
|
||||||
|
ProductSku = productSku;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return obj is Product product &&
|
||||||
|
Name == product.Name &&
|
||||||
|
EqualityComparer<ProductSku>.Default.Equals(ProductSku, product.ProductSku) &&
|
||||||
|
DeprecatedSkus.SequenceEqual(product.DeprecatedSkus) &&
|
||||||
|
ProductLines.SequenceEqual(product.ProductLines) &&
|
||||||
|
IsOnWarehouse == product.IsOnWarehouse &&
|
||||||
|
ProductMeasure == product.ProductMeasure &&
|
||||||
|
DeliveryMakeUp == product.DeliveryMakeUp &&
|
||||||
|
Price == product.Price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
HashCode hash = new HashCode();
|
||||||
|
hash.Add(Name);
|
||||||
|
hash.Add(ProductSku);
|
||||||
|
DeprecatedSkus.ForEach(x => hash.Add(x));
|
||||||
|
ProductLines.ForEach(x => hash.Add(x));
|
||||||
|
hash.Add(IsOnWarehouse);
|
||||||
|
hash.Add(ProductMeasure);
|
||||||
|
hash.Add(DeliveryMakeUp);
|
||||||
|
hash.Add(Price);
|
||||||
|
return hash.ToHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"({ProductSku}) {Name}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
194
RhSolutions.ProductSku/ProductSku.cs
Normal file
194
RhSolutions.ProductSku/ProductSku.cs
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace RhSolutions.Models;
|
||||||
|
public class ProductSku
|
||||||
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public string Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return $"1{Article}{Delimiter}{Variant}";
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var matches = GetMatches(value);
|
||||||
|
if (matches.Count == 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Wrong Id: {value}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var p = GetProducts(matches).First();
|
||||||
|
_article = p.Article;
|
||||||
|
_delimiter = p.Delimiter;
|
||||||
|
_variant = p.Variant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private const string matchPattern = @"(?<Lead>[1\s]|^|\b)(?<Article>\d{6})(?<Delimiter>[\s13-])(?<Variant>\d{3})(\b|$)";
|
||||||
|
private string _article;
|
||||||
|
private string _variant;
|
||||||
|
private char _delimiter = '1';
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
public ProductSku(string article, string delimiter, string variant)
|
||||||
|
{
|
||||||
|
_article = article;
|
||||||
|
_delimiter = delimiter[0];
|
||||||
|
_variant = variant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProductSku(string article, string variant)
|
||||||
|
{
|
||||||
|
_article = IsCorrectArticle(article) ? article
|
||||||
|
: throw new ArgumentException($"Wrong Article: {article}");
|
||||||
|
_variant = IsCorrectVariant(variant) ? variant
|
||||||
|
: throw new ArgumentException($"Wrong Variant: {variant}");
|
||||||
|
}
|
||||||
|
public ProductSku(string line)
|
||||||
|
{
|
||||||
|
var matches = GetMatches(line);
|
||||||
|
if (matches.Count == 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Wrong new Sku input {line}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var p = GetProducts(matches).First();
|
||||||
|
_article = p.Article;
|
||||||
|
_delimiter = p.Delimiter;
|
||||||
|
_variant = p.Variant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProductSku(Match match)
|
||||||
|
{
|
||||||
|
_article = match.Groups["Article"].Value;
|
||||||
|
_delimiter = match.Groups["Delimiter"].Value switch
|
||||||
|
{
|
||||||
|
"3" => '3',
|
||||||
|
_ => '1'
|
||||||
|
};
|
||||||
|
_variant = match.Groups["Variant"].Value;
|
||||||
|
}
|
||||||
|
public string Article
|
||||||
|
{
|
||||||
|
get => _article;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_article = IsCorrectArticle(value) ? value
|
||||||
|
: throw new ArgumentException($"Wrong Article: {value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string Variant
|
||||||
|
{
|
||||||
|
get => _variant;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_variant = IsCorrectVariant(value) ? value
|
||||||
|
: throw new ArgumentException($"Wrong Variant: {value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public char Delimiter
|
||||||
|
{
|
||||||
|
get => _delimiter;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != '1' || value != '3')
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Wrong Delimiter: {value}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_delimiter = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<ProductSku> Parse(string line)
|
||||||
|
{
|
||||||
|
MatchCollection matches = GetMatches(line);
|
||||||
|
if (matches.Count == 0)
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<ProductSku>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetProducts(matches);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryParse(string line, out IEnumerable<ProductSku> skus)
|
||||||
|
{
|
||||||
|
MatchCollection matches = GetMatches(line);
|
||||||
|
if (matches.Count == 0)
|
||||||
|
{
|
||||||
|
skus = Enumerable.Empty<ProductSku>();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
skus = GetProducts(matches);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MatchCollection GetMatches(string line)
|
||||||
|
{
|
||||||
|
return Regex.Matches(line, matchPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<ProductSku> GetProducts(MatchCollection matches)
|
||||||
|
{
|
||||||
|
foreach (Match match in matches)
|
||||||
|
{
|
||||||
|
yield return new ProductSku(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCorrectArticle(string line)
|
||||||
|
{
|
||||||
|
return line != null
|
||||||
|
&& line.Length == 6
|
||||||
|
&& line.All(c => char.IsDigit(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCorrectVariant(string line)
|
||||||
|
{
|
||||||
|
return line != null
|
||||||
|
&& line.Length == 3
|
||||||
|
&& line.All(c => char.IsDigit(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj == null || GetType() != obj.GetType())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductSku other = (ProductSku)obj;
|
||||||
|
|
||||||
|
return this.Article == other.Article &&
|
||||||
|
this.Delimiter == other.Delimiter &&
|
||||||
|
this.Variant == other.Variant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
HashCode hash = new();
|
||||||
|
hash.Add(_article);
|
||||||
|
hash.Add(_variant);
|
||||||
|
hash.Add(_delimiter);
|
||||||
|
return hash.ToHashCode();
|
||||||
|
}
|
||||||
|
}
|
20
RhSolutions.ProductSku/RhSolutions.ProductSku.csproj
Normal file
20
RhSolutions.ProductSku/RhSolutions.ProductSku.csproj
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>net472;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||||
|
<LangVersion>10.0</LangVersion>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<Version>1.0.3</Version>
|
||||||
|
<Authors>Serghei Cebotari</Authors>
|
||||||
|
<Product>RhSolutions Sku</Product>
|
||||||
|
<Description>Библиотека с классами моделей артикулов для плагинов и приложений RhSolutions</Description>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
62
RhSolutions.Tests/CanDoGuess.cs
Normal file
62
RhSolutions.Tests/CanDoGuess.cs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using RhSolutions.AddIn;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace RhSolutions.Tests;
|
||||||
|
|
||||||
|
[ExcelTestSettings(OutOfProcess = true)]
|
||||||
|
public class CanDoGuess : IDisposable
|
||||||
|
{
|
||||||
|
private RhSolutionsAddIn _addIn;
|
||||||
|
private IReader _guessReader;
|
||||||
|
private IReader _reader;
|
||||||
|
|
||||||
|
public CanDoGuess()
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable("ISTESTING", "true");
|
||||||
|
_addIn = new();
|
||||||
|
_addIn.AutoOpen();
|
||||||
|
_guessReader = new GuessReader(Util.Application);
|
||||||
|
_reader = new ExcelReader(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\TestSpecificationGuess.xlsx")]
|
||||||
|
public void CanWriteMultiplyRows()
|
||||||
|
{
|
||||||
|
Worksheet sourceSheet = Util.Workbook.Worksheets[1];
|
||||||
|
RhSolutionsAddIn.Configuration.SetPriceListPath(Path.GetFullPath(@"..\..\..\TestWorkbooks\TargetSpecificationGuess.xlsx"));
|
||||||
|
var products = _guessReader.ReadProducts(new[] { sourceSheet });
|
||||||
|
var _writer = new NewPriceWriter(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
_writer.WriteProducts(products);
|
||||||
|
Worksheet targetSheet = Util.Application.ActiveWindow.ActiveSheet;
|
||||||
|
var targetProducts = _reader.ReadProducts(new[] { targetSheet });
|
||||||
|
|
||||||
|
Assert.Equal("TestSpecificationGuess", products.First().Item1);
|
||||||
|
Assert.Equal("TargetSpecificationGuess", targetProducts.First().Item1);
|
||||||
|
Assert.Equal(products.First().Item2.Count(), targetProducts.First().Item2.Count());
|
||||||
|
Assert.Equal(products.First().Item2.Values.Sum(), targetProducts.First().Item2.Values.Sum());
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\TestSpecificationGuessOneRow.xlsx")]
|
||||||
|
public void CanWriteOneRow()
|
||||||
|
{
|
||||||
|
Worksheet sourceSheet = Util.Workbook.Worksheets[1];
|
||||||
|
RhSolutionsAddIn.Configuration.SetPriceListPath(Path.GetFullPath(@"..\..\..\TestWorkbooks\TargetSpecificationGuessOneRow.xlsx"));
|
||||||
|
var products = _guessReader.ReadProducts(new[] { sourceSheet });
|
||||||
|
var _writer = new NewPriceWriter(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
_writer.WriteProducts(products);
|
||||||
|
Worksheet targetSheet = Util.Application.ActiveWindow.ActiveSheet;
|
||||||
|
var targetProducts = _reader.ReadProducts(new[] { targetSheet });
|
||||||
|
|
||||||
|
Assert.Equal("TestSpecificationGuessOneRow", products.First().Item1);
|
||||||
|
Assert.Equal("TargetSpecificationGuessOneRow", targetProducts.First().Item1);
|
||||||
|
Assert.Equal(products.First().Item2.Count(), targetProducts.First().Item2.Count());
|
||||||
|
Assert.Equal(products.First().Item2.Values.Sum(), targetProducts.First().Item2.Values.Sum());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_addIn.AutoClose();
|
||||||
|
Util.Application.ActiveWindow.Close(SaveChanges: false);
|
||||||
|
}
|
||||||
|
}
|
41
RhSolutions.Tests/CanFillCouplings.cs
Normal file
41
RhSolutions.Tests/CanFillCouplings.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using RhSolutions.AddIn;
|
||||||
|
|
||||||
|
namespace RhSolutions.Tests;
|
||||||
|
|
||||||
|
[ExcelTestSettings(OutOfProcess = true)]
|
||||||
|
public class CanFillCouplings : IDisposable
|
||||||
|
{
|
||||||
|
private RhSolutionsAddIn _addIn;
|
||||||
|
private IFittingsCalculator _calculator;
|
||||||
|
private IReader _reader;
|
||||||
|
private IWriter _writer;
|
||||||
|
private Worksheet _worksheet;
|
||||||
|
|
||||||
|
public CanFillCouplings()
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable("ISTESTING", "true");
|
||||||
|
_addIn = new();
|
||||||
|
_addIn.AutoOpen();
|
||||||
|
_calculator = new CouplingsCalculator();
|
||||||
|
_reader = new ExcelReader(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
_writer = new CurrentPriceWriter(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
_worksheet = Util.Workbook.Worksheets[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\TestSpecificationCouplings.xlsx")]
|
||||||
|
public void CanCalculateSleeves()
|
||||||
|
{
|
||||||
|
var products = _reader.ReadProducts(new[] { _worksheet });
|
||||||
|
var couplings = _calculator.Calculate(products.First().Item2);
|
||||||
|
_writer.WriteProducts(couplings);
|
||||||
|
for (int i = 2; i < 14; i++)
|
||||||
|
{
|
||||||
|
Assert.Equal(_worksheet.Range[$"F{i}"].Value, _worksheet.Range[$"E{i}"].Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_addIn.AutoClose();
|
||||||
|
}
|
||||||
|
}
|
41
RhSolutions.Tests/CanFillSleeves.cs
Normal file
41
RhSolutions.Tests/CanFillSleeves.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using RhSolutions.AddIn;
|
||||||
|
|
||||||
|
namespace RhSolutions.Tests;
|
||||||
|
|
||||||
|
[ExcelTestSettings(OutOfProcess = true)]
|
||||||
|
public class CanFillSleeves : IDisposable
|
||||||
|
{
|
||||||
|
private RhSolutionsAddIn _addIn;
|
||||||
|
private IFittingsCalculator _calculator;
|
||||||
|
private IReader _reader;
|
||||||
|
private IWriter _writer;
|
||||||
|
private Worksheet _worksheet;
|
||||||
|
|
||||||
|
public CanFillSleeves()
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable("ISTESTING", "true");
|
||||||
|
_addIn = new();
|
||||||
|
_addIn.AutoOpen();
|
||||||
|
_calculator = new SleevesCalculator();
|
||||||
|
_reader = new ExcelReader(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
_writer = new CurrentPriceWriter(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
_worksheet = Util.Workbook.Worksheets[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\TestSpecificationSleeves.xlsx")]
|
||||||
|
public void CanCalculateSleeves()
|
||||||
|
{
|
||||||
|
var products = _reader.ReadProducts(new[] { _worksheet });
|
||||||
|
var sleeves = _calculator.Calculate(products.First().Item2);
|
||||||
|
_writer.WriteProducts(sleeves);
|
||||||
|
for (int i = 2; i < 14; i++)
|
||||||
|
{
|
||||||
|
Assert.Equal(_worksheet.Range[$"F{i}"].Value, _worksheet.Range[$"E{i}"].Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_addIn.AutoClose();
|
||||||
|
}
|
||||||
|
}
|
@ -12,10 +12,11 @@ public class CanReadProducts : IDisposable
|
|||||||
|
|
||||||
public CanReadProducts()
|
public CanReadProducts()
|
||||||
{
|
{
|
||||||
|
Environment.SetEnvironmentVariable("ISTESTING", "true");
|
||||||
_addIn = new();
|
_addIn = new();
|
||||||
_testWorkbook = Util.Application.Workbooks.Add();
|
_testWorkbook = Util.Application.Workbooks.Add();
|
||||||
_addIn.AutoOpen();
|
_addIn.AutoOpen();
|
||||||
_reader = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IReader>();
|
_reader = new ExcelReader(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ExcelFact]
|
[ExcelFact]
|
||||||
@ -45,14 +46,17 @@ public class CanReadProducts : IDisposable
|
|||||||
Assert.Equal(3, products.Count());
|
Assert.Equal(3, products.Count());
|
||||||
}
|
}
|
||||||
|
|
||||||
[ExcelFact(Workbook = @"TestWorkbooks\Specifications\HeatingFloor.xlsx")]
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\TestSpecification.xlsx")]
|
||||||
public void CanReadWorkbook()
|
public void CanReadWorkbook()
|
||||||
{
|
{
|
||||||
Worksheet worksheet = Util.Workbook.Worksheets[1];
|
Worksheet worksheet = Util.Workbook.Worksheets[1];
|
||||||
var products = _reader.ReadProducts(new[] { worksheet });
|
var result = _reader.ReadProducts(new[] { worksheet });
|
||||||
|
Assert.NotNull(result);
|
||||||
Assert.NotNull(products);
|
Assert.NotEmpty(result);
|
||||||
Assert.NotEmpty(products);
|
Assert.Equal("TestSpecification", result.First().Item1);
|
||||||
|
var products = result.First().Item2;
|
||||||
|
Assert.Equal(46, products.Count());
|
||||||
|
Assert.Equal(29266, products.Sum(p => p.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
116
RhSolutions.Tests/CanWriteProducts.cs
Normal file
116
RhSolutions.Tests/CanWriteProducts.cs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using RhSolutions.AddIn;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace RhSolutions.Tests;
|
||||||
|
|
||||||
|
[ExcelTestSettings(OutOfProcess = true)]
|
||||||
|
public class CanWriteProducts : IDisposable
|
||||||
|
{
|
||||||
|
private RhSolutionsAddIn _addIn;
|
||||||
|
private IReader _reader;
|
||||||
|
|
||||||
|
public CanWriteProducts()
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable("ISTESTING", "true");
|
||||||
|
_addIn = new();
|
||||||
|
_addIn.AutoOpen();
|
||||||
|
_reader = new ExcelReader(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\TestSpecification.xlsx")]
|
||||||
|
public void CanWriteSingle()
|
||||||
|
{
|
||||||
|
Worksheet sourceSheet = Util.Workbook.Worksheets[1];
|
||||||
|
RhSolutionsAddIn.Configuration.SetPriceListPath(Path.GetFullPath(@"..\..\..\TestWorkbooks\TargetSpecification.xlsx"));
|
||||||
|
var products = _reader.ReadProducts(new[] { sourceSheet });
|
||||||
|
var _writer = new NewPriceWriter(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
_writer.WriteProducts(products);
|
||||||
|
Worksheet targetSheet = Util.Application.ActiveWindow.ActiveSheet;
|
||||||
|
var targetProducts = _reader.ReadProducts(new[] { targetSheet });
|
||||||
|
|
||||||
|
Assert.Equal("TestSpecification", products.First().Item1);
|
||||||
|
Assert.Equal("TargetSpecification", targetProducts.First().Item1);
|
||||||
|
Assert.Equal(products.First().Item2.Count(), targetProducts.First().Item2.Count());
|
||||||
|
Assert.Equal(products.First().Item2.Values.Sum(), targetProducts.First().Item2.Values.Sum());
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\TestSpecificationMultipleProductLines.xlsx")]
|
||||||
|
public void CanWriteMultipleProductLines()
|
||||||
|
{
|
||||||
|
Worksheet sourceSheet = Util.Workbook.Worksheets[1];
|
||||||
|
RhSolutionsAddIn.Configuration.SetPriceListPath(Path.GetFullPath(@"..\..\..\TestWorkbooks\TargetSpecificationMultipleProductLines.xlsx"));
|
||||||
|
var products = _reader.ReadProducts(new[] { sourceSheet });
|
||||||
|
var _writer = new NewPriceWriter(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
_writer.WriteProducts(products);
|
||||||
|
Worksheet targetSheet = Util.Application.ActiveWindow.ActiveSheet;
|
||||||
|
var targetProducts = _reader.ReadProducts(new[] { targetSheet });
|
||||||
|
|
||||||
|
Assert.Equal("TestSpecificationMultipleProductLines", products.First().Item1);
|
||||||
|
Assert.Equal("TargetSpecificationMultipleProductLines", targetProducts.First().Item1);
|
||||||
|
Assert.Equal(2, targetProducts.First().Item2.Count());
|
||||||
|
Assert.True(Enumerable.SequenceEqual(products.First().Item2, targetProducts.First().Item2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\TestSpecificationNotFound.xlsx")]
|
||||||
|
public void CanWriteNotFound()
|
||||||
|
{
|
||||||
|
Worksheet sourceSheet = Util.Workbook.Worksheets[1];
|
||||||
|
RhSolutionsAddIn.Configuration.SetPriceListPath(Path.GetFullPath(@"..\..\..\TestWorkbooks\TargetSpecificationNotFound.xlsx"));
|
||||||
|
var products = _reader.ReadProducts(new[] { sourceSheet });
|
||||||
|
var _writer = new NewPriceWriter(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
_writer.WriteProducts(products);
|
||||||
|
Worksheet targetSheet = Util.Application.ActiveWindow.ActiveSheet;
|
||||||
|
|
||||||
|
Assert.Equal("???", targetSheet.Range["B4"].Value2);
|
||||||
|
Assert.Contains("Молот Тора", targetSheet.Range["C4"].Value2);
|
||||||
|
Assert.Contains("15555551555", targetSheet.Range["C4"].Value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\TestSpecificationReplaced.xlsx")]
|
||||||
|
public void CanWriteReplaced()
|
||||||
|
{
|
||||||
|
Worksheet sourceSheet = Util.Workbook.Worksheets[1];
|
||||||
|
RhSolutionsAddIn.Configuration.SetPriceListPath(Path.GetFullPath(@"..\..\..\TestWorkbooks\TargetSpecificationReplaced.xlsx"));
|
||||||
|
var products = _reader.ReadProducts(new[] { sourceSheet });
|
||||||
|
var _writer = new NewPriceWriter(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
_writer.WriteProducts(products);
|
||||||
|
Worksheet targetSheet = Util.Application.ActiveWindow.ActiveSheet;
|
||||||
|
var targetProducts = _reader.ReadProducts(new[] { targetSheet });
|
||||||
|
|
||||||
|
Assert.Equal("TestSpecificationReplaced", products.First().Item1);
|
||||||
|
Assert.Equal("TargetSpecificationReplaced", targetProducts.First().Item1);
|
||||||
|
var result = targetProducts.First().Item2.ToArray();
|
||||||
|
Assert.Contains("Молот Тора", result[0].Key.Name);
|
||||||
|
Assert.Contains("15555551555", result[0].Key.Name);
|
||||||
|
Assert.Equal(1, result[0].Value);
|
||||||
|
Assert.Contains("Нога Вирта", result[1].Key.Name);
|
||||||
|
Assert.Contains("17777771777", result[1].Key.Name);
|
||||||
|
Assert.Equal(1, result[1].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\TestSpecificationNewVariant.xlsx")]
|
||||||
|
public void CanWriteNewVariant()
|
||||||
|
{
|
||||||
|
Worksheet sourceSheet = Util.Workbook.Worksheets[1];
|
||||||
|
RhSolutionsAddIn.Configuration.SetPriceListPath(Path.GetFullPath(@"..\..\..\TestWorkbooks\TargetSpecificationNewVariant.xlsx"));
|
||||||
|
var products = _reader.ReadProducts(new[] { sourceSheet });
|
||||||
|
var _writer = new NewPriceWriter(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
_writer.WriteProducts(products);
|
||||||
|
Worksheet targetSheet = Util.Application.ActiveWindow.ActiveSheet;
|
||||||
|
var targetProducts = _reader.ReadProducts(new[] { targetSheet });
|
||||||
|
|
||||||
|
Assert.Equal("TestSpecificationNewVariant", products.First().Item1);
|
||||||
|
Assert.Equal("TargetSpecificationNewVariant", targetProducts.First().Item1);
|
||||||
|
var result = targetProducts.First().Item2.ToArray();
|
||||||
|
Assert.Contains("Молот Тора", result[0].Key.Name);
|
||||||
|
Assert.Contains("11201111555", result[0].Key.Name);
|
||||||
|
Assert.Equal(1, result[0].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_addIn.AutoClose();
|
||||||
|
Util.Application.ActiveWindow.Close(SaveChanges: false);
|
||||||
|
}
|
||||||
|
}
|
42
RhSolutions.Tests/RealPricelistTest.cs
Normal file
42
RhSolutions.Tests/RealPricelistTest.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using RhSolutions.AddIn;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace RhSolutions.Tests;
|
||||||
|
|
||||||
|
[ExcelTestSettings(OutOfProcess = true)]
|
||||||
|
public class RealPricelistTest : IDisposable
|
||||||
|
{
|
||||||
|
private RhSolutionsAddIn _addIn;
|
||||||
|
private IReader _reader;
|
||||||
|
|
||||||
|
public RealPricelistTest()
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable("ISTESTING", "true");
|
||||||
|
_addIn = new();
|
||||||
|
_addIn.AutoOpen();
|
||||||
|
_reader = new ExcelReader(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\RealTestSpecification.xlsm")]
|
||||||
|
public void CanWrite()
|
||||||
|
{
|
||||||
|
Worksheet sourceSheet = Util.Workbook.Worksheets[1];
|
||||||
|
RhSolutionsAddIn.Configuration.SetPriceListPath(Path.GetFullPath(@"..\..\..\TestWorkbooks\RealTargetSpecification.xlsx"));
|
||||||
|
var products = _reader.ReadProducts(new[] { sourceSheet });
|
||||||
|
var _writer = new NewPriceWriter(Util.Application, RhSolutionsAddIn.Configuration);
|
||||||
|
_writer.WriteProducts(products);
|
||||||
|
Worksheet targetSheet = Util.Application.ActiveWindow.ActiveSheet;
|
||||||
|
targetSheet.Range["A1"].Formula = "=SUM(H:H)";
|
||||||
|
|
||||||
|
Assert.Equal("RealTestSpecification", products.First().Item1);
|
||||||
|
Assert.Equal("RealTargetSpecification.xlsx", Util.Application.ActiveWorkbook.Name);
|
||||||
|
Assert.Equal(1188.0, targetSheet.Range["A1"].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_addIn.AutoClose();
|
||||||
|
Util.Application.ActiveWindow.Close(SaveChanges: false);
|
||||||
|
}
|
||||||
|
}
|
@ -10,26 +10,12 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ExcelDna.Interop" Version="15.0.1" />
|
<PackageReference Include="ExcelDna.Interop" Version="15.0.1" />
|
||||||
<PackageReference Include="ExcelDna.Testing" Version="1.6.0" />
|
<PackageReference Include="ExcelDna.Testing" Version="1.8.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\RhSolutions.AddIn\RhSolutions.AddIn.csproj" />
|
<ProjectReference Include="..\RhSolutions.AddIn\RhSolutions.AddIn.csproj" />
|
||||||
</ItemGroup>
|
<ProjectReference Include="..\RhSolutions.ProductSku\RhSolutions.ProductSku.csproj" />
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Update="TestWorkbooks\EmptyTestTable.xlsx">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="TestWorkbooks\EmptyWorkbook.xlsx">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="TestWorkbooks\ExcelTableTest.xlsx">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="TestWorkbooks\Specifications\HeatingFloor.xlsx">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
171
RhSolutions.Tests/SkuTests.cs
Normal file
171
RhSolutions.Tests/SkuTests.cs
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
using RhSolutions.Models;
|
||||||
|
|
||||||
|
namespace RhSolutions.Tests;
|
||||||
|
|
||||||
|
public class SkuTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void EqualityTest()
|
||||||
|
{
|
||||||
|
Product p1 = new("11600011001")
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
ProductLines = new List<string>
|
||||||
|
{
|
||||||
|
"TestLine"
|
||||||
|
},
|
||||||
|
IsOnWarehouse = true,
|
||||||
|
ProductMeasure = Measure.Kg,
|
||||||
|
DeliveryMakeUp = 100.0,
|
||||||
|
Price = 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
Product p2 = new("11600011001")
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
ProductLines = new List<string>
|
||||||
|
{
|
||||||
|
"TestLine"
|
||||||
|
},
|
||||||
|
IsOnWarehouse = true,
|
||||||
|
ProductMeasure = Measure.Kg,
|
||||||
|
DeliveryMakeUp = 100.0,
|
||||||
|
Price = 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
Product p3 = new("11600013001")
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
ProductLines = new List<string>
|
||||||
|
{
|
||||||
|
"TestLine"
|
||||||
|
},
|
||||||
|
IsOnWarehouse = true,
|
||||||
|
ProductMeasure = Measure.Kg,
|
||||||
|
DeliveryMakeUp = 100.0,
|
||||||
|
Price = 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
Assert.True(p1.Equals(p2));
|
||||||
|
Assert.False(p1.Equals(p3));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ProductHashCodeTest()
|
||||||
|
{
|
||||||
|
Product p1 = new("11600011001")
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
ProductLines = new List<string>
|
||||||
|
{
|
||||||
|
"TestLine"
|
||||||
|
},
|
||||||
|
IsOnWarehouse = true,
|
||||||
|
ProductMeasure = Measure.Kg,
|
||||||
|
DeliveryMakeUp = 100.0,
|
||||||
|
Price = 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
Product p2 = new("11600011001")
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
ProductLines = new List<string>
|
||||||
|
{
|
||||||
|
"TestLine"
|
||||||
|
},
|
||||||
|
IsOnWarehouse = true,
|
||||||
|
ProductMeasure = Measure.Kg,
|
||||||
|
DeliveryMakeUp = 100.0,
|
||||||
|
Price = 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
Product p3 = new("11600013001")
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
ProductLines = new List<string>
|
||||||
|
{
|
||||||
|
"TestLine"
|
||||||
|
},
|
||||||
|
IsOnWarehouse = true,
|
||||||
|
ProductMeasure = Measure.Kg,
|
||||||
|
DeliveryMakeUp = 100.0,
|
||||||
|
Price = 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
int hash1 = p1.GetHashCode();
|
||||||
|
int hash2 = p2.GetHashCode();
|
||||||
|
int hash3 = p3.GetHashCode();
|
||||||
|
Assert.True(hash1 == hash2);
|
||||||
|
Assert.False(hash1 == hash3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SkuEqualityTest()
|
||||||
|
{
|
||||||
|
ProductSku sku1 = new("160001", "001");
|
||||||
|
ProductSku sku2 = new("11600011001");
|
||||||
|
Assert.True(sku1.Equals(sku2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SkuHashCodeTest()
|
||||||
|
{
|
||||||
|
ProductSku sku1 = new("160001", "001");
|
||||||
|
ProductSku sku2 = new("11600011001");
|
||||||
|
int hash1 = sku1.GetHashCode();
|
||||||
|
int hash2 = sku2.GetHashCode();
|
||||||
|
Assert.True(hash1 == hash2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("12222221333")]
|
||||||
|
[InlineData("222222-333")]
|
||||||
|
[InlineData("222222 333")]
|
||||||
|
[InlineData("string 12222221333")]
|
||||||
|
[InlineData("12222221333 string")]
|
||||||
|
[InlineData("string 12222221333 string")]
|
||||||
|
[InlineData("string 222222-333")]
|
||||||
|
[InlineData("222222-333 string")]
|
||||||
|
[InlineData("string 222222-333 string")]
|
||||||
|
[InlineData("string 222222 333")]
|
||||||
|
[InlineData("222222 333 string")]
|
||||||
|
[InlineData("string 222222 333 string")]
|
||||||
|
public void StandardSkuParseTest(string line)
|
||||||
|
{
|
||||||
|
Assert.True(ProductSku.TryParse(line, out var skus));
|
||||||
|
Assert.True(skus.First().Article == "222222"
|
||||||
|
&& skus.First().Variant == "333"
|
||||||
|
&& skus.First().Delimiter == '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("12222223444")]
|
||||||
|
[InlineData("string 12222223444")]
|
||||||
|
[InlineData("12222223444 string")]
|
||||||
|
[InlineData("string 12222223444 string")]
|
||||||
|
public void NewSkuParseTest(string line)
|
||||||
|
{
|
||||||
|
Assert.True(ProductSku.TryParse(line, out var skus));
|
||||||
|
Assert.True(skus.First().Article == "222222"
|
||||||
|
&& skus.First().Variant == "444"
|
||||||
|
&& skus.First().Delimiter == '3');
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("160001-001 11384611001 160002 002 11600033003")]
|
||||||
|
public void MultipleParseTest(string line)
|
||||||
|
{
|
||||||
|
Assert.True(ProductSku.TryParse(line, out var skus));
|
||||||
|
Assert.Equal(4, skus.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("160001 001")]
|
||||||
|
[InlineData("160001*001")]
|
||||||
|
[InlineData("160001001")]
|
||||||
|
[InlineData("31600011001")]
|
||||||
|
public void DoesntParse(string line)
|
||||||
|
{
|
||||||
|
Assert.False(ProductSku.TryParse(line, out _));
|
||||||
|
}
|
||||||
|
}
|
BIN
RhSolutions.Tests/TestWorkbooks/RealTargetSpecification.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/RealTargetSpecification.xlsx
Normal file
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/RealTestSpecification.xlsm
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/RealTestSpecification.xlsm
Normal file
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/TargetSpecification.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TargetSpecification.xlsx
Normal file
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/TargetSpecificationGuess.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TargetSpecificationGuess.xlsx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/TargetSpecificationNotFound.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TargetSpecificationNotFound.xlsx
Normal file
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/TargetSpecificationReplaced.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TargetSpecificationReplaced.xlsx
Normal file
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecification.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecification.xlsx
Normal file
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationCouplings.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationCouplings.xlsx
Normal file
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationGuess.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationGuess.xlsx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationNewVariant.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationNewVariant.xlsx
Normal file
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationNotFound.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationNotFound.xlsx
Normal file
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationReplaced.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationReplaced.xlsx
Normal file
Binary file not shown.
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationSleeves.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationSleeves.xlsx
Normal file
Binary file not shown.
@ -10,19 +10,20 @@ public class WorkbookValidationTests : IDisposable
|
|||||||
|
|
||||||
public WorkbookValidationTests()
|
public WorkbookValidationTests()
|
||||||
{
|
{
|
||||||
|
Environment.SetEnvironmentVariable("ISTESTING", "true");
|
||||||
_addIn = new RhSolutionsAddIn();
|
_addIn = new RhSolutionsAddIn();
|
||||||
_addIn.AutoOpen();
|
_addIn.AutoOpen();
|
||||||
Util.Application.Workbooks.Add();
|
Util.Application.Workbooks.Add();
|
||||||
}
|
}
|
||||||
|
|
||||||
[ExcelFact(Workbook = @"TestWorkbooks\EmptyTestTable.xlsx")]
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\EmptyTestTable.xlsx")]
|
||||||
public void WorksheetIsCorrect()
|
public void WorksheetIsCorrect()
|
||||||
{
|
{
|
||||||
Worksheet worksheet = Util.Workbook.Sheets[1];
|
Worksheet worksheet = Util.Workbook.Sheets[1];
|
||||||
Assert.True(worksheet.IsValidSource());
|
Assert.True(worksheet.IsValidSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
[ExcelFact(Workbook = @"TestWorkbooks\EmptyWorkbook.xlsx")]
|
[ExcelFact(Workbook = @"..\..\..\TestWorkbooks\EmptyWorkbook.xlsx")]
|
||||||
public void EmptyWorkbookIsNotCorrect()
|
public void EmptyWorkbookIsNotCorrect()
|
||||||
{
|
{
|
||||||
Worksheet worksheet = Util.Workbook.Sheets[1];
|
Worksheet worksheet = Util.Workbook.Sheets[1];
|
||||||
|
@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RhSolutions.AddIn", "RhSolu
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RhSolutions.Tests", "RhSolutions.Tests\RhSolutions.Tests.csproj", "{6EECCDDB-741C-404A-874F-BB8656265162}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RhSolutions.Tests", "RhSolutions.Tests\RhSolutions.Tests.csproj", "{6EECCDDB-741C-404A-874F-BB8656265162}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RhSolutions.ProductSku", "RhSolutions.ProductSku\RhSolutions.ProductSku.csproj", "{59CD05D0-71E0-4027-968A-8BE89A6FDCEF}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -21,6 +23,10 @@ Global
|
|||||||
{6EECCDDB-741C-404A-874F-BB8656265162}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{6EECCDDB-741C-404A-874F-BB8656265162}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{6EECCDDB-741C-404A-874F-BB8656265162}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{6EECCDDB-741C-404A-874F-BB8656265162}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{6EECCDDB-741C-404A-874F-BB8656265162}.Release|Any CPU.Build.0 = Release|Any CPU
|
{6EECCDDB-741C-404A-874F-BB8656265162}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{59CD05D0-71E0-4027-968A-8BE89A6FDCEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{59CD05D0-71E0-4027-968A-8BE89A6FDCEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{59CD05D0-71E0-4027-968A-8BE89A6FDCEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{59CD05D0-71E0-4027-968A-8BE89A6FDCEF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
Loading…
Reference in New Issue
Block a user