Compare commits
54 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 |
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"
|
||||
},
|
||||
]
|
||||
}
|
14
Install.ps1
14
Install.ps1
@ -1,14 +0,0 @@
|
||||
$object = Invoke-WebRequest -Uri "https://gitea.cebotari.ru/api/v1/repos/chebser/RhSolutions-AddIn/releases/latest" | ConvertFrom-Json
|
||||
$download_link = $object.assets[1].browser_download_url
|
||||
Invoke-WebRequest -Uri $download_link -OutFile "$env:appdata\Microsoft\AddIns\RhSolutions-AddIn-packed.xll"
|
||||
|
||||
$excel = New-Object -ComObject Excel.Application
|
||||
foreach ($addin in $excel.AddIns2)
|
||||
{
|
||||
$title = $addin.Title
|
||||
if ($title -eq 'RhSolutions Add-In')
|
||||
{
|
||||
$addin.Installed = $true
|
||||
}
|
||||
}
|
||||
$excel.Quit()
|
@ -8,12 +8,13 @@
|
||||
- Объединение нескольких прайс-листов в один файл
|
||||
- Поиск пар артикул-количество в любой сторонней таблице и экспорт в таблицу заказов
|
||||
- Подбор монтажных гильз для фитингов RAUTITAN
|
||||
- Подбор ремонтных муфт по количеству трубы
|
||||
- Экспорт таблицы заказов в девятиграфку по ГОСТ
|
||||
|
||||
*Для работы функций "Экспорт", "Актуализация" и "Объединение" требуется указать путь к файлу пустого прайс-листа РЕХАУ*
|
||||
|
||||
## Реализованные формулы для работы с артикулами
|
||||
- ```=RHSOLUTIONS()``` и ```=РЕХАУ()``` - поиск в удаленной базе данных
|
||||
- ```=РЕХАУ()``` - поиск артикула РЕХАУ по произвольному запросу в сервисе [RhSolutions-Api](https://gitea.cebotari.ru/chebser/RhSolutions-Api)
|
||||
- ```=РЕХАУАРТИКУЛ()``` - экстракция артикула РЕХАУ из любой строки по регулярному выражению
|
||||
- ```=РЕХАУИМЯ()``` - поиск названия артикула по номеру
|
||||
- ```=РЕХАУЦЕНА()``` - поиск цены в евро по номеру артикула
|
||||
|
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,14 +1,7 @@
|
||||
using System.Net;
|
||||
using ExcelDna.IntelliSense;
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
|
||||
namespace RhSolutions.AddIn;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
public sealed class RhSolutionsAddIn : IExcelAddIn
|
||||
{
|
||||
public static Application Excel { get; private set; }
|
||||
@ -25,7 +18,6 @@ public sealed class RhSolutionsAddIn : IExcelAddIn
|
||||
.AddSingleton<IAddInConfiguration, AddInConfiguration>()
|
||||
.AddSingleton<IDatabaseClient, DatabaseClient>()
|
||||
.AddSingleton<ICurrencyClient, CurrencyClient>()
|
||||
.AddSingleton<ISleevesCalculator, SleevesCalculator>()
|
||||
.AddTransient<IFileDialog, FileDialog>();
|
||||
|
||||
Services.AddSingleton<WriterFactory>();
|
||||
@ -42,6 +34,12 @@ public sealed class RhSolutionsAddIn : IExcelAddIn
|
||||
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>();
|
||||
|
||||
ServiceProvider = Services.BuildServiceProvider();
|
||||
@ -50,34 +48,13 @@ public sealed class RhSolutionsAddIn : IExcelAddIn
|
||||
|
||||
EventsUtil.Initialize();
|
||||
|
||||
bool isTesting = Environment.GetEnvironmentVariable("ISTESTING") switch
|
||||
{
|
||||
"true" => true,
|
||||
"false" => false,
|
||||
_ => false
|
||||
};
|
||||
if (!isTesting)
|
||||
{
|
||||
IntelliSenseServer.Install();
|
||||
}
|
||||
|
||||
ServicePointManager.SecurityProtocol =
|
||||
SecurityProtocolType.Tls12;
|
||||
ServicePointManager.DefaultConnectionLimit = 50;
|
||||
}
|
||||
|
||||
public void AutoClose()
|
||||
{
|
||||
EventsUtil.Uninitialize();
|
||||
|
||||
bool isTesting = Environment.GetEnvironmentVariable("ISTESTING") switch
|
||||
{
|
||||
"true" => true,
|
||||
"false" => false,
|
||||
_ => false
|
||||
};
|
||||
if (!isTesting)
|
||||
{
|
||||
IntelliSenseServer.Uninstall();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,99 +1,76 @@
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
namespace RhSolutions.AddIn;
|
||||
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
public static class RhSolutionsFunctions
|
||||
{
|
||||
private static readonly IDatabaseClient databaseClient =
|
||||
RhSolutionsAddIn.ServiceProvider.GetService<IDatabaseClient>();
|
||||
private static readonly ICurrencyClient currencyClient =
|
||||
RhSolutionsAddIn.ServiceProvider.GetRequiredService<ICurrencyClient>();
|
||||
|
||||
namespace RhSolutions.AddIn;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
public class RhSolutionsFunctions
|
||||
[ExcelFunction(Name = "РЕХАУ")]
|
||||
public static object ProductSearch(object[,] values)
|
||||
{
|
||||
[ExcelFunction(Description = "Поиск артикула в базе данных")]
|
||||
public static object RHSOLUTIONS([ExcelArgument(Name = "СТРОКА", Description = "Ячейка с артикулом РЕХАУ или поисковый запрос в свободной форме")] string line)
|
||||
List<string> strings = new();
|
||||
int rows = values.GetLength(0);
|
||||
int columns = values.GetLength(1);
|
||||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
IDatabaseClient databaseClient = RhSolutionsAddIn.ServiceProvider.GetService<IDatabaseClient>();
|
||||
|
||||
ProductSku.TryParse(line, out var skus);
|
||||
|
||||
if (ExcelAsyncUtil.Run(nameof(RHSOLUTIONS), line, delegate
|
||||
for (int column = 0; column < columns; column++)
|
||||
{
|
||||
return databaseClient.GetProducts(line)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}) is not IEnumerable<Product> requestResult)
|
||||
{
|
||||
if (skus.Any())
|
||||
{
|
||||
return $"{skus.First()} ...";
|
||||
object value = values[row, column];
|
||||
strings.Add(value.ToString());
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
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 (!requestResult.Any() && !skus.Any())
|
||||
else if (!products.Any())
|
||||
{
|
||||
return ExcelError.ExcelErrorNA;
|
||||
}
|
||||
else if (!requestResult.Any())
|
||||
{
|
||||
return $"{skus.First()}";
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstProduct = requestResult.First();
|
||||
return $"{firstProduct.ProductSku} {firstProduct.Name}";
|
||||
}
|
||||
var product = products.First();
|
||||
return $"{product.Id} {product.Name}";
|
||||
}
|
||||
}
|
||||
|
||||
[ExcelFunction(Description = "Поиск артикула в базе данных")]
|
||||
public static object РЕХАУ([ExcelArgument(Name = "СТРОКА", Description = "Ячейка с артикулом РЕХАУ или поисковый запрос в свободной форме")] string line)
|
||||
=> RHSOLUTIONS(line);
|
||||
|
||||
[ExcelFunction(Description = "Выделить артикул РЕХАУ из ячейки")]
|
||||
public static object РЕХАУАРТИКУЛ([ExcelArgument(Name = "СТРОКА", Description = "Ячейка содержащая артикул РЕХАУ")] string line)
|
||||
[ExcelFunction(Name = "РЕХАУАРТИКУЛ")]
|
||||
public static object SkuSearch(string query)
|
||||
{
|
||||
if (ProductSku.TryParse(line, out var skus))
|
||||
if (ProductSku.TryParse(query, out var skus))
|
||||
{
|
||||
return skus.First().Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ExcelError.ExcelErrorNA;
|
||||
}
|
||||
}
|
||||
|
||||
[ExcelFunction(Description = "Поиск названия по артикулу РЕХАУ")]
|
||||
public static object РЕХАУИМЯ([ExcelArgument(Name = "АРТИКУЛ", Description = "Ячейка содержащая артикул РЕХАУ")] string line)
|
||||
[ExcelFunction(Name = "РЕХАУИМЯ")]
|
||||
public static object GetProductName(string query)
|
||||
{
|
||||
if (!ProductSku.TryParse(line, out var skus))
|
||||
if (!ProductSku.TryParse(query, out var skus))
|
||||
{
|
||||
return ExcelError.ExcelErrorNA;
|
||||
}
|
||||
else
|
||||
{
|
||||
var article = skus.First().Id;
|
||||
IDatabaseClient databaseClient = RhSolutionsAddIn.ServiceProvider.GetService<IDatabaseClient>();
|
||||
|
||||
if (ExcelAsyncUtil.Run(nameof(РЕХАУИМЯ), line, delegate
|
||||
var functionName = nameof(GetProductName);
|
||||
var parameters = new object[] { query };
|
||||
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
|
||||
{
|
||||
return databaseClient.GetProducts(article)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
return await databaseClient.GetProducts(article);
|
||||
}) is not IEnumerable<Product> requestResult)
|
||||
{
|
||||
return "Загрузка...";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!requestResult.Any())
|
||||
else if (!requestResult.Any())
|
||||
{
|
||||
return ExcelError.ExcelErrorNA;
|
||||
}
|
||||
@ -103,33 +80,26 @@ public class RhSolutionsFunctions
|
||||
return firstProduct.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ExcelFunction(Description = "Поиск цены артикула РЕХАУ в евро")]
|
||||
public static object РЕХАУЦЕНА([ExcelArgument(Name = "АРТИКУЛ", Description = "Ячейка содержащая артикул РЕХАУ")] string line)
|
||||
[ExcelFunction(Name = "РЕХАУЦЕНА")]
|
||||
public static object GetProductPrice(string query)
|
||||
{
|
||||
if (!ProductSku.TryParse(line, out var skus))
|
||||
if (!ProductSku.TryParse(query, out var skus))
|
||||
{
|
||||
return ExcelError.ExcelErrorNA;
|
||||
}
|
||||
else
|
||||
{
|
||||
var article = skus.First().Id;
|
||||
IDatabaseClient databaseClient = RhSolutionsAddIn.ServiceProvider.GetService<IDatabaseClient>();
|
||||
var functionName = nameof(GetProductPrice);
|
||||
var parameters = new object[] { article };
|
||||
|
||||
if (ExcelAsyncUtil.Run(nameof(РЕХАУЦЕНА), line, delegate
|
||||
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
|
||||
{
|
||||
return databaseClient.GetProducts(article)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
return await databaseClient.GetProducts(article);
|
||||
}) is not IEnumerable<Product> requestResult)
|
||||
{
|
||||
return "Загрузка...";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!requestResult.Any())
|
||||
else if (!requestResult.Any())
|
||||
{
|
||||
return ExcelError.ExcelErrorNA;
|
||||
}
|
||||
@ -139,65 +109,42 @@ public class RhSolutionsFunctions
|
||||
return Math.Round(firstProduct.Price * 1.2m, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ExcelFunction(Description = "Поиск цены артикула РЕХАУ в рублях")]
|
||||
public static object РЕХАУЦЕНАРУБ([ExcelArgument(Name = "АРТИКУЛ", Description = "Ячейка содержащая артикул РЕХАУ")] string line,
|
||||
[ExcelArgument(Name = "ДАТА", Description = "Дата в формате Excel (необязательно)")] double dateField)
|
||||
[ExcelFunction(Name = "РЕХАУЦЕНАРУБ")]
|
||||
public static object GetProductPriceRub(string query, double dateField)
|
||||
{
|
||||
if (!ProductSku.TryParse(line, out var skus))
|
||||
if (!ProductSku.TryParse(query, out var skus))
|
||||
{
|
||||
return ExcelError.ExcelErrorNA;
|
||||
}
|
||||
else
|
||||
{
|
||||
IDatabaseClient databaseClient = RhSolutionsAddIn.ServiceProvider.GetService<IDatabaseClient>();
|
||||
ICurrencyClient currencyClient = RhSolutionsAddIn.ServiceProvider.GetRequiredService<ICurrencyClient>();
|
||||
IMemoryCache memoryCache = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IMemoryCache>();
|
||||
var article = skus.First().Id;
|
||||
DateTime date = dateField == 0 ? DateTime.Today : DateTime.FromOADate(dateField);
|
||||
|
||||
if (!memoryCache.TryGetValue(date, out decimal exchangeRate))
|
||||
{
|
||||
var result = ExcelAsyncUtil.Run(nameof(КУРСЕВРО), dateField, delegate
|
||||
{
|
||||
var requestResult = currencyClient.GetExchangeRate(date)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
|
||||
var functionName = nameof(GetProductPriceRub);
|
||||
var parameters = new object[] { date };
|
||||
|
||||
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
|
||||
{
|
||||
var requestResult = await currencyClient.GetExchangeRate(date);
|
||||
return requestResult ?? -1m;
|
||||
});
|
||||
|
||||
if (result is not decimal)
|
||||
}) is not decimal exchangeRate)
|
||||
{
|
||||
return "Загрузка...";
|
||||
}
|
||||
else
|
||||
{
|
||||
exchangeRate = (decimal)result;
|
||||
var cacheEntryOptions = new MemoryCacheEntryOptions()
|
||||
.SetSlidingExpiration(TimeSpan.FromHours(1));
|
||||
memoryCache.Set(date, exchangeRate, cacheEntryOptions);
|
||||
}
|
||||
}
|
||||
|
||||
if (ExcelAsyncUtil.Run(nameof(РЕХАУЦЕНАРУБ), line, delegate
|
||||
parameters = new object[] { query };
|
||||
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
|
||||
{
|
||||
var product = databaseClient.GetProducts(article)
|
||||
.GetAwaiter()
|
||||
.GetResult()
|
||||
.FirstOrDefault();
|
||||
|
||||
var products = await databaseClient.GetProducts(article);
|
||||
var product = products.FirstOrDefault();
|
||||
return product == null ? -1m :
|
||||
product.Price * exchangeRate * 1.2m;
|
||||
product.Price * (decimal)exchangeRate * 1.2m;
|
||||
}) is not decimal requestResult)
|
||||
{
|
||||
return "Загрузка...";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (requestResult < 0)
|
||||
else if (requestResult < 0 || exchangeRate < 0)
|
||||
{
|
||||
return ExcelError.ExcelErrorNA;
|
||||
}
|
||||
@ -207,39 +154,3 @@ public class RhSolutionsFunctions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ExcelFunction(Description = "Получить курс евро по ЦБ")]
|
||||
public static object КУРСЕВРО([ExcelArgument(Name = "ДАТА", Description = "Дата в формате Excel (необязательно)")] double dateField)
|
||||
{
|
||||
ICurrencyClient currencyClient = RhSolutionsAddIn.ServiceProvider.GetRequiredService<ICurrencyClient>();
|
||||
IMemoryCache memoryCache = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IMemoryCache>();
|
||||
DateTime date = dateField == 0 ? DateTime.Today : DateTime.FromOADate(dateField);
|
||||
|
||||
if (!memoryCache.TryGetValue(date, out decimal exchangeRate))
|
||||
{
|
||||
var result = ExcelAsyncUtil.Run(nameof(КУРСЕВРО), dateField, delegate
|
||||
{
|
||||
var requestResult = currencyClient.GetExchangeRate(date)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
|
||||
return requestResult ?? -1m;
|
||||
});
|
||||
|
||||
if (result is not decimal)
|
||||
{
|
||||
return "Загрузка...";
|
||||
}
|
||||
else
|
||||
{
|
||||
exchangeRate = (decimal)result;
|
||||
var cacheEntryOptions = new MemoryCacheEntryOptions()
|
||||
.SetSlidingExpiration(TimeSpan.FromHours(1));
|
||||
memoryCache.Set(date, exchangeRate, cacheEntryOptions);
|
||||
}
|
||||
}
|
||||
|
||||
return exchangeRate < 0 ? ExcelError.ExcelErrorNA : exchangeRate;
|
||||
}
|
||||
}
|
@ -1,16 +1,11 @@
|
||||
using ExcelDna.Integration.CustomUI;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
#if! NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace RhSolutions.Controllers;
|
||||
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
[ComVisible(true)]
|
||||
public class RibbonController : ExcelRibbon
|
||||
{
|
||||
@ -20,20 +15,25 @@ public class RibbonController : ExcelRibbon
|
||||
public override string GetCustomUI(string RibbonID)
|
||||
{
|
||||
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>
|
||||
<tabs>
|
||||
<tab id='rau' label='RhSolutions'>
|
||||
<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='merge' label='Объединить' size='normal' imageMso='Copy' onAction='OnToolPressed'/>
|
||||
<button id='guess' getEnabled='GetGuessEnabled' label='Найти и экспортировать' size='normal' imageMso='ControlWizards' onAction='OnToolPressed'/>
|
||||
<button id='fillsleeves' getEnabled='GetSleevesEnabled' label='Подобрать гильзы' size='normal' imageMso='CreateQueryFromWizard' 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 id='rausettings' getLabel='GetVersionLabel'>
|
||||
<button id='setPriceList' getLabel='GetPriceListPathLabel' size='large' imageMso='TableExcelSpreadsheetInsert' onAction='OnSetPricePressed'/>
|
||||
<group id='fittingsCalc' label='Расчет фитингов'>
|
||||
<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>
|
||||
</tab>
|
||||
</tabs>
|
||||
@ -85,7 +85,7 @@ public class RibbonController : ExcelRibbon
|
||||
|
||||
public bool GetConvertEnabled(IRibbonControl control) => _workbookIsValid;
|
||||
public bool GetDxfEnabled(IRibbonControl control) => _workbookIsValid;
|
||||
public bool GetSleevesEnabled(IRibbonControl control) => _workbookIsValid;
|
||||
public bool GetFittingsCalcEnabled(IRibbonControl control) => _workbookIsValid;
|
||||
public bool GetGuessEnabled(IRibbonControl control) => RhSolutionsAddIn.Excel.ActiveWorkbook != null && !_workbookIsValid;
|
||||
|
||||
public bool GetExportEnabled(IRibbonControl control)
|
||||
@ -109,7 +109,7 @@ public class RibbonController : ExcelRibbon
|
||||
public string GetPriceListPathLabel(IRibbonControl control)
|
||||
{
|
||||
string name = RhSolutionsAddIn.Configuration.GetPriceListFileName();
|
||||
return string.IsNullOrEmpty(name) ? "Шаблонный файл" : name;
|
||||
return string.IsNullOrEmpty(name) ? "Указать шаблонный файл" : name;
|
||||
}
|
||||
|
||||
public static void UpdateWorkbookValidation()
|
||||
@ -123,4 +123,13 @@ public class RibbonController : ExcelRibbon
|
||||
_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
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.8.5.4")]
|
||||
[assembly: AssemblyFileVersion("1.8.5.4")]
|
||||
[assembly: AssemblyVersion("1.9.5.1")]
|
||||
[assembly: AssemblyFileVersion("1.9.5.1")]
|
||||
|
@ -3,7 +3,7 @@
|
||||
"Excel": {
|
||||
"commandName": "Executable",
|
||||
"executablePath": "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE",
|
||||
"commandLineArgs": "RhSolutions-AddIn64.xll"
|
||||
"commandLineArgs": "/x \"RhSolutions-AddIn64.xll\""
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +1,30 @@
|
||||
<?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">
|
||||
<ExternalLibrary Path="RhSolutions.AddIn.dll" ExplicitExports="false" LoadFromBytes="true" Pack="true" IncludePdb="false" />
|
||||
<Reference Path="ExcelDna.IntelliSense.dll" Pack="true" />
|
||||
<Reference Path="Microsoft.AspNetCore.Http.Abstractions.dll" Pack="true" />
|
||||
<Reference Path="Microsoft.AspNetCore.Http.Extensions.dll" Pack="true" />
|
||||
<Reference Path="Microsoft.AspNetCore.Http.Features.dll" Pack="true" />
|
||||
<Reference Path="ExcelDna.Registration.dll" Pack="true" />
|
||||
<Reference Path="Microsoft.Bcl.AsyncInterfaces.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.dll" Pack="true" />
|
||||
<Reference Path="Microsoft.Extensions.FileProviders.Abstractions.dll" Pack="true" />
|
||||
<Reference Path="Microsoft.Extensions.Http.dll" Pack="true" />
|
||||
<Reference Path="Microsoft.Extensions.Logging.Abstractions.dll" Pack="true" />
|
||||
<Reference Path="Microsoft.Extensions.Logging.dll" Pack="true" />
|
||||
<Reference Path="Microsoft.Extensions.Options.dll" Pack="true" />
|
||||
<Reference Path="Microsoft.Extensions.Primitives.dll" Pack="true" />
|
||||
<Reference Path="Microsoft.Net.Http.Headers.dll" Pack="true" />
|
||||
<Reference Path="netDxf.dll" Pack="true" />
|
||||
<Reference Path="Newtonsoft.Json.dll" Pack="true" />
|
||||
<Reference Path="netDxf.dll" Pack="true" />
|
||||
<Reference Path="RhSolutions.ProductSku.dll" Pack="true" />
|
||||
<Reference Path="System.Buffers.dll" Pack="true" />
|
||||
<Reference Path="System.Diagnostics.DiagnosticSource.dll" Pack="true" />
|
||||
<Reference Path="System.Memory.dll" Pack="true" />
|
||||
<Reference Path="System.Numerics.Vectors.dll" Pack="true" />
|
||||
<Reference Path="System.Runtime.CompilerServices.Unsafe.dll" Pack="true" />
|
||||
<Reference Path="System.Text.Encodings.Web.dll" Pack="true" />
|
||||
<Reference Path="System.Threading.Tasks.Extensions.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>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472</TargetFrameworks>
|
||||
<TargetFrameworks>net472;net6.0-windows</TargetFrameworks>
|
||||
<LangVersion>10</LangVersion>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>RhSolutions</RootNamespace>
|
||||
@ -8,40 +8,37 @@
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
|
||||
<ExcelDnaPackCompressResources>false</ExcelDnaPackCompressResources>
|
||||
</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>
|
||||
<PackageReference Include="ExcelDna.AddIn" Version="1.6.0">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="ExcelDna.Integration" Version="1.6.0" />
|
||||
<PackageReference Include="ExcelDna.IntelliSense" Version="1.6.0" />
|
||||
<PackageReference Include="ExcelDna.AddIn" Version="1.8.0" />
|
||||
<PackageReference Include="ExcelDna.Integration" Version="1.8.0" />
|
||||
<PackageReference Include="ExcelDna.Interop" Version="15.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
|
||||
<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.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.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="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.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||
</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>
|
@ -1,114 +1,43 @@
|
||||
using System.Configuration;
|
||||
using Microsoft.Win32;
|
||||
using System.IO;
|
||||
|
||||
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()
|
||||
{
|
||||
_priceListHeaders = new Dictionary<string, string>()
|
||||
_rootKey = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\RhSolutions\RhSolutions-AddIn");
|
||||
_priceListPath = (string)_rootKey.GetValue("PriceListPath");
|
||||
_priceListHeaders = new()
|
||||
{
|
||||
["Amount"] = AmountHeader,
|
||||
["OldSku"] = OldSkuHeader,
|
||||
["Sku"] = SkuHeader,
|
||||
["ProductLine"] = ProductLineHeader,
|
||||
["Name"] = NameHeader,
|
||||
["Measure"] = MeasureHeader
|
||||
["Amount"] = "Кол-во",
|
||||
["OldSku"] = "Прежний материал",
|
||||
["Sku"] = "Актуальный материал",
|
||||
["ProductLine"] = "Программа",
|
||||
["Name"] = "Наименование",
|
||||
["Measure"] = "Ед. изм."
|
||||
};
|
||||
}
|
||||
|
||||
[UserScopedSetting]
|
||||
[DefaultSettingValue("Кол-во")]
|
||||
public string AmountHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)this[nameof(AmountHeader)];
|
||||
}
|
||||
}
|
||||
|
||||
[UserScopedSetting]
|
||||
[DefaultSettingValue("Прежний материал")]
|
||||
public string OldSkuHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)this[nameof(OldSkuHeader)];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[UserScopedSetting]
|
||||
[DefaultSettingValue("Актуальный материал")]
|
||||
public string SkuHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)this[nameof(SkuHeader)];
|
||||
}
|
||||
}
|
||||
|
||||
[UserScopedSetting]
|
||||
[DefaultSettingValue("Программа")]
|
||||
public string ProductLineHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)this[nameof(ProductLineHeader)];
|
||||
}
|
||||
}
|
||||
|
||||
[UserScopedSetting]
|
||||
[DefaultSettingValue("Наименование")]
|
||||
public string NameHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)this[nameof(NameHeader)];
|
||||
}
|
||||
}
|
||||
|
||||
[UserScopedSetting]
|
||||
[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 string GetPriceListFileName() => Path.GetFileName(_priceListPath);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Net;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
@ -8,15 +9,19 @@ 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)
|
||||
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);
|
||||
@ -25,11 +30,13 @@ public class CurrencyClient : ICurrencyClient
|
||||
response.EnsureSuccessStatusCode();
|
||||
string xml = await response.Content.ReadAsStringAsync();
|
||||
XElement valCourses = XElement.Parse(xml);
|
||||
|
||||
decimal? exchangeRate = decimal.Parse(valCourses.Elements("Valute")
|
||||
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
|
||||
@ -38,4 +45,6 @@ public class CurrencyClient : ICurrencyClient
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return exchangeRate;
|
||||
}
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Newtonsoft.Json;
|
||||
using System.Web;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
@ -56,11 +54,11 @@ public class DatabaseClient : IDatabaseClient
|
||||
|
||||
else
|
||||
{
|
||||
QueryBuilder qb = new()
|
||||
UriBuilder builder = new(@"https://rh.cebotari.ru/api/search")
|
||||
{
|
||||
{"query", line}
|
||||
Query = $"query={line.Replace("&", "%26")}"
|
||||
};
|
||||
string request = @"https://rh.cebotari.ru/api/search" + qb.ToQueryString();
|
||||
string request = builder.Uri.AbsoluteUri;
|
||||
|
||||
if (!_memoryCache.TryGetValue(line, out IEnumerable<Product> products))
|
||||
{
|
||||
|
@ -79,7 +79,7 @@ public class ExcelReader : IReader, IDisposable
|
||||
return readResult;
|
||||
}
|
||||
|
||||
public List<(string, Dictionary<Product, double>)>
|
||||
public IEnumerable<(string, Dictionary<Product, double>)>
|
||||
ReadProducts(IEnumerable<Worksheet> worksheets)
|
||||
{
|
||||
List<(string, Dictionary<Product, double>)> result = new();
|
||||
@ -151,11 +151,15 @@ public class ExcelReader : IReader, IDisposable
|
||||
|
||||
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);
|
||||
List<Worksheet> worksheets = new();
|
||||
|
||||
@ -170,7 +174,11 @@ public class ExcelReader : IReader, IDisposable
|
||||
var result = ReadProducts(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;
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ namespace RhSolutions.Services
|
||||
.Insert(XlInsertShiftDirection.xlShiftToRight, XlInsertFormatOrigin.xlFormatFromRightOrBelow);
|
||||
|
||||
Range newColumnHeader = _worksheet.Cells[_amountCell.Row, _amountCell.Column - 1];
|
||||
newColumnHeader.NumberFormat = "@";
|
||||
newColumnHeader.Value2 = $"{product.Item1}";
|
||||
newColumnHeader.WrapText = true;
|
||||
|
||||
|
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}")
|
||||
};
|
||||
}
|
||||
}
|
@ -192,7 +192,7 @@ public class GuessReader : IReader
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<(string, Dictionary<Product, double>)> ReadProducts(IEnumerable<Worksheet> worksheets)
|
||||
public IEnumerable<(string, Dictionary<Product, double>)> ReadProducts(IEnumerable<Worksheet> worksheets)
|
||||
{
|
||||
List<(string, Dictionary<Product, double>)> result = new();
|
||||
foreach (Worksheet worksheet in worksheets)
|
||||
@ -206,7 +206,7 @@ public class GuessReader : IReader
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<(string, Dictionary<Product, double>)> ReadProducts(string[] files)
|
||||
public IEnumerable<(string, Dictionary<Product, double>)> ReadProducts(string[] files)
|
||||
{
|
||||
_progressBar = new("Открываю исходные файлы...", files.Length);
|
||||
List<Worksheet> worksheets = new();
|
||||
|
@ -1,13 +1,12 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace RhSolutions.Services;
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
public interface IAddInConfiguration
|
||||
{
|
||||
public string GetPriceListPath();
|
||||
public void SetPriceListPath(string value);
|
||||
public string GetPriceListFileName();
|
||||
public Dictionary<string, string> GetPriceListHeaders();
|
||||
public event SettingChangingEventHandler OnSettingsChange;
|
||||
public void SetPriceListPath(string value);
|
||||
public delegate void SettingsHandler();
|
||||
public event SettingsHandler OnSettingsChange;
|
||||
public void SaveSettings();
|
||||
}
|
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 Dictionary<Product, double> ReadProducts(Range range);
|
||||
public List<(string, Dictionary<Product, double>)> ReadProducts(IEnumerable<Worksheet> worksheets);
|
||||
public List<(string, Dictionary<Product, double>)> ReadProducts(string[] files);
|
||||
public IEnumerable<(string, Dictionary<Product, double>)> ReadProducts(IEnumerable<Worksheet> worksheets);
|
||||
public IEnumerable<(string, Dictionary<Product, double>)> ReadProducts(string[] files);
|
||||
new void Dispose();
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
public interface ISleevesCalculator
|
||||
{
|
||||
public Dictionary<Product, double> CalculateSleeves(Dictionary<Product, double> products);
|
||||
}
|
@ -2,14 +2,14 @@
|
||||
|
||||
namespace RhSolutions.Services;
|
||||
|
||||
public class SleevesCalculator : ISleevesCalculator
|
||||
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> CalculateSleeves(Dictionary<Product, double> products)
|
||||
public Dictionary<Product, double> Calculate(Dictionary<Product, double> products)
|
||||
{
|
||||
Dictionary<string, double> result = new()
|
||||
{
|
||||
@ -20,6 +20,11 @@ public class SleevesCalculator : ISleevesCalculator
|
||||
["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"));
|
||||
|
||||
@ -30,9 +35,16 @@ public class SleevesCalculator : ISleevesCalculator
|
||||
{
|
||||
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);
|
||||
@ -40,23 +52,35 @@ public class SleevesCalculator : ISleevesCalculator
|
||||
{
|
||||
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("11600011001"),
|
||||
"20" => new Product("11600021001"),
|
||||
"25" => new Product("11600031001"),
|
||||
"32" => new Product("11600041001"),
|
||||
"40" => new Product("11600051001"),
|
||||
"50" => new Product("11397711002"),
|
||||
"63" => new Product("11397811002"),
|
||||
"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);
|
||||
}
|
||||
|
@ -1,20 +1,12 @@
|
||||
using Microsoft.Office.Interop.Excel;
|
||||
using RhSolutions.AddIn;
|
||||
using RhSolutions.Controllers;
|
||||
using System.Configuration;
|
||||
#if !NET472
|
||||
using System.Runtime.Versioning;
|
||||
#endif
|
||||
using RhSolutions.Controllers;
|
||||
|
||||
namespace RhSolutions.Tools;
|
||||
|
||||
namespace RhSolutions.Tools
|
||||
{
|
||||
#if !NET472
|
||||
[SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
internal static class EventsUtil
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
RibbonController.EnsurePriseListExists();
|
||||
RhSolutionsAddIn.Excel.SheetSelectionChange += RefreshExportButton;
|
||||
RhSolutionsAddIn.Excel.SheetActivate += RefreshButtons;
|
||||
RhSolutionsAddIn.Excel.WorkbookActivate += RefreshButtons;
|
||||
@ -36,6 +28,7 @@ namespace RhSolutions.Tools
|
||||
RibbonController.RefreshControl("dxfexport");
|
||||
RibbonController.RefreshControl("guess");
|
||||
RibbonController.RefreshControl("fillsleeves");
|
||||
RibbonController.RefreshControl("fillcouplings");
|
||||
}
|
||||
|
||||
private static void RefreshExportButton(object sh, Range target)
|
||||
@ -43,9 +36,8 @@ namespace RhSolutions.Tools
|
||||
RibbonController.RefreshControl("export");
|
||||
}
|
||||
|
||||
private static void RefreshSettingTitle(object sender, SettingChangingEventArgs e)
|
||||
private static void RefreshSettingTitle()
|
||||
{
|
||||
RibbonController.RefreshControl("setPriceList");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
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);
|
||||
}
|
||||
}
|
@ -17,9 +17,12 @@ internal class MergeTool : Tool
|
||||
{
|
||||
IFileDialog dialog = RhSolutionsAddIn.ServiceProvider.GetRequiredService<IFileDialog>();
|
||||
string[] files = dialog.GetFiles();
|
||||
if (files.Length > 0)
|
||||
{
|
||||
_reader = _readerFactory.GetReader("Excel");
|
||||
var products = _reader.ReadProducts(files);
|
||||
_writer = _writerFactory.GetWriter("NewPrice");
|
||||
_writer.WriteProducts(products);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
namespace RhSolutions.Tools;
|
||||
|
||||
internal class SleevesTool : Tool
|
||||
{
|
||||
private readonly ISleevesCalculator _sleevesCaluculator;
|
||||
|
||||
public SleevesTool(ReaderFactory readerFactory, WriterFactory writerFactory, ISleevesCalculator sleevesCaluculator) : base(readerFactory, writerFactory)
|
||||
{
|
||||
_sleevesCaluculator = sleevesCaluculator;
|
||||
}
|
||||
|
||||
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 sleeves = _sleevesCaluculator.CalculateSleeves(products.Select(p => p.Item2).First());
|
||||
_writer = _writerFactory.GetWriter("CurrentPrice");
|
||||
_writer.WriteProducts(sleeves);
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ internal class ToolFactory
|
||||
{
|
||||
static ReaderFactory readerFactory = RhSolutionsAddIn.ServiceProvider.GetService<ReaderFactory>();
|
||||
static WriterFactory writerFactory = RhSolutionsAddIn.ServiceProvider.GetService<WriterFactory>();
|
||||
static ISleevesCalculator sleevesCaluculator = RhSolutionsAddIn.ServiceProvider.GetService<ISleevesCalculator>();
|
||||
static FittingsCalculatorFactory fittingsCalculatorFactory = RhSolutionsAddIn.ServiceProvider.GetService<FittingsCalculatorFactory>();
|
||||
|
||||
public Tool GetTool(string toolName)
|
||||
{
|
||||
@ -15,7 +15,8 @@ internal class ToolFactory
|
||||
"merge" => new MergeTool(readerFactory, writerFactory),
|
||||
"dxfexport" => new DxfTool(readerFactory, writerFactory),
|
||||
"guess" => new GuessTool(readerFactory, writerFactory),
|
||||
"fillsleeves" => new SleevesTool(readerFactory, writerFactory, sleevesCaluculator),
|
||||
"fillsleeves" => new FittingsTool(readerFactory, writerFactory, fittingsCalculatorFactory, "Sleeves"),
|
||||
"fillcouplings" => new FittingsTool(readerFactory, writerFactory, fittingsCalculatorFactory, "Couplings"),
|
||||
_ => throw new Exception($"Неизвестный инструмент {toolName}"),
|
||||
};
|
||||
return tool;
|
||||
|
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>
|
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<RunSettings>
|
||||
<RunConfiguration>
|
||||
<EnvironmentVariables>
|
||||
<ISTESTING>true</ISTESTING>
|
||||
</EnvironmentVariables>
|
||||
</RunConfiguration>
|
||||
</RunSettings>
|
@ -13,6 +13,7 @@ public class CanDoGuess : IDisposable
|
||||
|
||||
public CanDoGuess()
|
||||
{
|
||||
Environment.SetEnvironmentVariable("ISTESTING", "true");
|
||||
_addIn = new();
|
||||
_addIn.AutoOpen();
|
||||
_guessReader = new GuessReader(Util.Application);
|
||||
|
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();
|
||||
}
|
||||
}
|
@ -6,13 +6,14 @@ namespace RhSolutions.Tests;
|
||||
public class CanFillSleeves : IDisposable
|
||||
{
|
||||
private RhSolutionsAddIn _addIn;
|
||||
private ISleevesCalculator _calculator;
|
||||
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();
|
||||
@ -25,16 +26,12 @@ public class CanFillSleeves : IDisposable
|
||||
public void CanCalculateSleeves()
|
||||
{
|
||||
var products = _reader.ReadProducts(new[] { _worksheet });
|
||||
var sleeves = _calculator.CalculateSleeves(products.First().Item2);
|
||||
var sleeves = _calculator.Calculate(products.First().Item2);
|
||||
_writer.WriteProducts(sleeves);
|
||||
|
||||
Assert.Equal(25, _worksheet.Range["E2"].Value);
|
||||
Assert.Equal(15, _worksheet.Range["E3"].Value);
|
||||
Assert.Equal(7, _worksheet.Range["E4"].Value);
|
||||
Assert.Equal(8, _worksheet.Range["E5"].Value);
|
||||
Assert.Equal(1, _worksheet.Range["E6"].Value);
|
||||
Assert.Equal(3, _worksheet.Range["E7"].Value);
|
||||
Assert.Equal(4, _worksheet.Range["E8"].Value);
|
||||
for (int i = 2; i < 14; i++)
|
||||
{
|
||||
Assert.Equal(_worksheet.Range[$"F{i}"].Value, _worksheet.Range[$"E{i}"].Value);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -12,6 +12,7 @@ public class CanReadProducts : IDisposable
|
||||
|
||||
public CanReadProducts()
|
||||
{
|
||||
Environment.SetEnvironmentVariable("ISTESTING", "true");
|
||||
_addIn = new();
|
||||
_testWorkbook = Util.Application.Workbooks.Add();
|
||||
_addIn.AutoOpen();
|
||||
|
@ -12,6 +12,7 @@ public class CanWriteProducts : IDisposable
|
||||
|
||||
public CanWriteProducts()
|
||||
{
|
||||
Environment.SetEnvironmentVariable("ISTESTING", "true");
|
||||
_addIn = new();
|
||||
_addIn.AutoOpen();
|
||||
_reader = new ExcelReader(Util.Application, RhSolutionsAddIn.Configuration);
|
||||
|
@ -12,6 +12,7 @@ public class RealPricelistTest : IDisposable
|
||||
|
||||
public RealPricelistTest()
|
||||
{
|
||||
Environment.SetEnvironmentVariable("ISTESTING", "true");
|
||||
_addIn = new();
|
||||
_addIn.AutoOpen();
|
||||
_reader = new ExcelReader(Util.Application, RhSolutionsAddIn.Configuration);
|
||||
|
@ -10,11 +10,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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>
|
||||
<ProjectReference Include="..\RhSolutions.AddIn\RhSolutions.AddIn.csproj" />
|
||||
<ProjectReference Include="..\RhSolutions.ProductSku\RhSolutions.ProductSku.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</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/TestSpecificationCouplings.xlsx
Normal file
BIN
RhSolutions.Tests/TestWorkbooks/TestSpecificationCouplings.xlsx
Normal file
Binary file not shown.
Binary file not shown.
@ -10,6 +10,7 @@ public class WorkbookValidationTests : IDisposable
|
||||
|
||||
public WorkbookValidationTests()
|
||||
{
|
||||
Environment.SetEnvironmentVariable("ISTESTING", "true");
|
||||
_addIn = new RhSolutionsAddIn();
|
||||
_addIn.AutoOpen();
|
||||
Util.Application.Workbooks.Add();
|
||||
|
@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RhSolutions.AddIn", "RhSolu
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RhSolutions.Tests", "RhSolutions.Tests\RhSolutions.Tests.csproj", "{6EECCDDB-741C-404A-874F-BB8656265162}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RhSolutions.ProductSku", "RhSolutions.ProductSku\RhSolutions.ProductSku.csproj", "{59CD05D0-71E0-4027-968A-8BE89A6FDCEF}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.Release|Any CPU.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
Loading…
Reference in New Issue
Block a user