Compare commits

...

14 Commits

23 changed files with 156 additions and 88 deletions

26
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
// 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
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}

12
.vscode/tasks.json vendored Normal file
View 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"
},
]
}

View File

@ -14,7 +14,7 @@
*Для работы функций "Экспорт", "Актуализация" и "Объединение" требуется указать путь к файлу пустого прайс-листа РЕХАУ*
## Реализованные формулы для работы с артикулами
- ```=RHSOLUTIONS()``` и ```=РЕХАУ()``` - поиск в удаленной базе данных
- ```=РЕХАУ()``` - поиск артикула РЕХАУ по произвольному запросу в сервисе [RhSolutions-Api](https://gitea.cebotari.ru/chebser/RhSolutions-Api)
- ```=РЕХАУАРТИКУЛ()``` - экстракция артикула РЕХАУ из любой строки по регулярному выражению
- ```=РЕХАУИМЯ()``` - поиск названия артикула по номеру
- ```=РЕХАУЦЕНА()``` - поиск цены в евро по номеру артикула

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

View File

@ -1,6 +1,4 @@
using ExcelDna.Registration.Utils;
namespace RhSolutions.AddIn;
namespace RhSolutions.AddIn;
public static class RhSolutionsFunctions
{
@ -9,12 +7,12 @@ public static class RhSolutionsFunctions
private static readonly ICurrencyClient currencyClient =
RhSolutionsAddIn.ServiceProvider.GetRequiredService<ICurrencyClient>();
[ExcelFunction]
public static object РЕХАУ(string query)
[ExcelFunction(Name = "РЕХАУ")]
public static object ProductSearch(string query)
{
var functionName = nameof(РЕХАУ);
var functionName = nameof(ProductSearch);
var parameters = new object[] { query };
if (AsyncTaskUtil.RunTask(functionName, parameters, async () =>
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
{
return await databaseClient.GetProducts(query);
}) is not IEnumerable<Product> products)
@ -32,8 +30,8 @@ public static class RhSolutionsFunctions
}
}
[ExcelFunction]
public static object РЕХАУАРТИКУЛ(string query)
[ExcelFunction(Name = "РЕХАУАРТИКУЛ")]
public static object SkuSearch(string query)
{
if (ProductSku.TryParse(query, out var skus))
{
@ -42,17 +40,17 @@ public static class RhSolutionsFunctions
return ExcelError.ExcelErrorNA;
}
[ExcelFunction]
public static object РЕХАУИМЯ(string query)
[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(РЕХАУИМЯ);
var functionName = nameof(GetProductName);
var parameters = new object[] { query };
if (AsyncTaskUtil.RunTask(functionName, parameters, async () =>
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
{
return await databaseClient.GetProducts(article);
}) is not IEnumerable<Product> requestResult)
@ -70,18 +68,18 @@ public static class RhSolutionsFunctions
}
}
[ExcelFunction]
public static object РЕХАУЦЕНА(string query)
[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(РЕХАУЦЕНА);
var functionName = nameof(GetProductPrice);
var parameters = new object[] { article };
if (AsyncTaskUtil.RunTask(functionName, parameters, async () =>
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
{
return await databaseClient.GetProducts(article);
}) is not IEnumerable<Product> requestResult)
@ -99,8 +97,8 @@ public static class RhSolutionsFunctions
}
}
[ExcelFunction]
public static object РЕХАУЦЕНАРУБ(string query, double dateField)
[ExcelFunction(Name = "РЕХАУЦЕНАРУБ")]
public static object GetProductPriceRub(string query, double dateField)
{
if (!ProductSku.TryParse(query, out var skus))
{
@ -110,10 +108,10 @@ public static class RhSolutionsFunctions
DateTime date = dateField == 0 ? DateTime.Today : DateTime.FromOADate(dateField);
var functionName = nameof(РЕХАУЦЕНАРУБ);
var functionName = nameof(GetProductPriceRub);
var parameters = new object[] { date };
if (AsyncTaskUtil.RunTask(functionName, parameters, async () =>
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
{
var requestResult = await currencyClient.GetExchangeRate(date);
return requestResult ?? -1m;
@ -123,7 +121,7 @@ public static class RhSolutionsFunctions
}
parameters = new object[] { query };
if (AsyncTaskUtil.RunTask(functionName, parameters, async () =>
if (ExcelAsyncUtil.RunTask(functionName, parameters, async () =>
{
var products = await databaseClient.GetProducts(article);
var product = products.FirstOrDefault();
@ -142,25 +140,4 @@ public static class RhSolutionsFunctions
return Math.Round(requestResult, 2);
}
}
[ExcelFunction]
public static object КУРСЕВРО(double dateField)
{
DateTime date = dateField == 0 ? DateTime.Today : DateTime.FromOADate(dateField);
var functionName = nameof(КУРСЕВРО);
var parameters = new object[] { date };
var exchangeRate = AsyncTaskUtil.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;
}
}

View File

@ -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
{
@ -114,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()
@ -128,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);
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 900 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -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.9.3.0")]
[assembly: AssemblyFileVersion("1.9.3.0")]
[assembly: AssemblyVersion("1.9.5.0")]
[assembly: AssemblyFileVersion("1.9.5.0")]

View File

@ -10,10 +10,10 @@
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ExcelDna.AddIn" Version="1.7.0" />
<PackageReference Include="ExcelDna.Integration" Version="1.7.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="ExcelDna.Registration" Version="1.7.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" />

View File

@ -38,10 +38,10 @@ public class CouplingsCalculator : IFittingsCalculator
.ToDictionary(kvp =>
kvp.Key switch
{
"16" => new Product("11600111001"),
"20" => new Product("11600121001"),
"25" => new Product("11600131001"),
"32" => new Product("11600141001"),
"16" => new Product("11080111001"),
"20" => new Product("11080121001"),
"25" => new Product("11080131001"),
"32" => new Product("11080141001"),
"40" => new Product("11600151001"),
"50" => new Product("14563021001"),
"63" => new Product("14563031001"),

View File

@ -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,10 +151,10 @@ 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>()

View File

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

View File

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

View File

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

View File

@ -17,6 +17,10 @@ public class SleevesCalculator : IFittingsCalculator
["20"] = 0,
["25"] = 0,
["32"] = 0,
["16PX"] = 0,
["20PX"] = 0,
["25PX"] = 0,
["32PX"] = 0,
["40"] = 0,
["50"] = 0,
["63"] = 0,
@ -31,7 +35,14 @@ public class SleevesCalculator : IFittingsCalculator
CaptureCollection collection = doubleCollection[0].Groups["Sleeve"].Captures;
foreach (Capture sleeve in collection)
{
result[sleeve.Value] += kvp.Value * 2;
if (kvp.Key.Name.Contains("PX") && sleeve.Value != "40")
{
result[$"{sleeve.Value}PX"] += kvp.Value * 2;
}
else
{
result[sleeve.Value] += kvp.Value * 2;
}
}
continue;
}
@ -41,7 +52,14 @@ public class SleevesCalculator : IFittingsCalculator
CaptureCollection collection = singleCollection[0].Groups["Sleeve"].Captures;
foreach (Capture sleeve in collection)
{
result[sleeve.Value] += kvp.Value;
if (kvp.Key.Name.Contains("PX") && sleeve.Value != "40")
{
result[$"{sleeve.Value}PX"] += kvp.Value;
}
else
{
result[sleeve.Value] += kvp.Value;
}
}
}
}
@ -50,13 +68,17 @@ public class SleevesCalculator : IFittingsCalculator
.ToDictionary(kvp =>
kvp.Key switch
{
"16" => new Product("11600011001"),
"20" => new Product("11600021001"),
"25" => new Product("11600031001"),
"32" => new Product("11600041001"),
"16" => new Product("11080011001"),
"20" => new Product("11080021001"),
"25" => new Product("11080031001"),
"32" => new Product("11080041001"),
"16PX" => new Product("11600011001"),
"20PX" => new Product("11600021001"),
"25PX" => new Product("11600031001"),
"32PX" => new Product("11600041001"),
"40" => new Product("11600051001"),
"50" => new Product("11397711002"),
"63" => new Product("11397811002"),
"50" => new Product("11397713002"),
"63" => new Product("11397813002"),
_ => throw new Exception($"Неизвестный диаметр {kvp.Key}")
}, kvp => kvp.Value);
}

View File

@ -1,17 +1,12 @@
using RhSolutions.Controllers;
#if !NET472
using System.Runtime.Versioning;
#endif
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;

View File

@ -29,13 +29,17 @@ public class CanFillSleeves : IDisposable
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(22, _worksheet.Range["E2"].Value);
Assert.Equal(12, _worksheet.Range["E3"].Value);
Assert.Equal(5, _worksheet.Range["E4"].Value);
Assert.Equal(6, _worksheet.Range["E5"].Value);
Assert.Equal(3, _worksheet.Range["E6"].Value);
Assert.Equal(3, _worksheet.Range["E7"].Value);
Assert.Equal(4, _worksheet.Range["E8"].Value);
Assert.Equal(2, _worksheet.Range["E8"].Value);
Assert.Equal(2, _worksheet.Range["E9"].Value);
Assert.Equal(1, _worksheet.Range["E10"].Value);
Assert.Equal(3, _worksheet.Range["E11"].Value);
Assert.Equal(4, _worksheet.Range["E12"].Value);
}
public void Dispose()

View File

@ -10,7 +10,7 @@
<ItemGroup>
<PackageReference Include="ExcelDna.Interop" Version="15.0.1" />
<PackageReference Include="ExcelDna.Testing" Version="1.7.0" />
<PackageReference Include="ExcelDna.Testing" Version="1.8.0" />
</ItemGroup>
<ItemGroup>