Implement BS parser
This commit is contained in:
parent
a542bfb7f4
commit
be127319e2
@ -6,26 +6,28 @@ namespace RhSolutions.SkuParser.Controllers;
|
|||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("/api/[controller]")]
|
[Route("/api/[controller]")]
|
||||||
public class CommonParseController : ControllerBase
|
public class ParseController : ControllerBase
|
||||||
{
|
{
|
||||||
private IServiceProvider _provider;
|
private IServiceProvider _provider;
|
||||||
private Dictionary<Product, double> _result;
|
private Dictionary<Product, double> _result;
|
||||||
public CommonParseController(IServiceProvider provider)
|
public ParseController(IServiceProvider provider)
|
||||||
{
|
{
|
||||||
_provider = provider;
|
_provider = provider;
|
||||||
_result = new();
|
_result = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult PostFiles()
|
public IActionResult PostFiles([FromQuery] bool bs = false)
|
||||||
{
|
{
|
||||||
IFormFileCollection files = Request.Form.Files;
|
IFormFileCollection files = Request.Form.Files;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
ISkuParser parser = _provider.GetRequiredKeyedService<ISkuParser>(file.ContentType);
|
ISkuParser parser = bs ? _provider.GetRequiredKeyedService<ISkuParser>("BS")
|
||||||
|
: _provider.GetRequiredKeyedService<ISkuParser>(file.ContentType);
|
||||||
var dict = parser.ParseProducts(file);
|
var dict = parser.ParseProducts(file);
|
||||||
|
|
||||||
foreach (var kvp in dict)
|
foreach (var kvp in dict)
|
||||||
{
|
{
|
||||||
if (_result.ContainsKey(kvp.Key))
|
if (_result.ContainsKey(kvp.Key))
|
||||||
@ -43,6 +45,13 @@ public class CommonParseController : ControllerBase
|
|||||||
{
|
{
|
||||||
return BadRequest(error: $"{ex.Message}\n\n{ex.Source}\n{ex.StackTrace}");
|
return BadRequest(error: $"{ex.Message}\n\n{ex.Source}\n{ex.StackTrace}");
|
||||||
}
|
}
|
||||||
return new JsonResult(_result.Select(x => new { x.Key.Sku, x.Value }));
|
return new JsonResult(_result.Select(x => new
|
||||||
|
{
|
||||||
|
x.Key.Sku,
|
||||||
|
x.Key.ProductLine,
|
||||||
|
x.Key.Name,
|
||||||
|
x.Key.Price,
|
||||||
|
quantity = x.Value
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,10 @@ namespace RhSolutions.SkuParser.Models;
|
|||||||
|
|
||||||
public record Product
|
public record Product
|
||||||
{
|
{
|
||||||
|
private string _sku = string.Empty;
|
||||||
|
private const string _parsePattern = @"(?<Lead>[1\s]|^|\b)(?<Article>\d{6})(?<Delimiter>[\s13-])(?<Variant>\d{3})(\b|$)";
|
||||||
|
private const string _validnessPattern = @"^1\d{6}[1|3]\d{3}$";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Артикул РЕХАУ в заданном формате
|
/// Артикул РЕХАУ в заданном формате
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -17,9 +21,9 @@ public record Product
|
|||||||
: throw new ArgumentException("$Неверный артикул: {value}");
|
: throw new ArgumentException("$Неверный артикул: {value}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private string _sku = string.Empty;
|
public ProductLine? ProductLine { get; set; }
|
||||||
private const string _parsePattern = @"(?<Lead>[1\s]|^|\b)(?<Article>\d{6})(?<Delimiter>[\s13-])(?<Variant>\d{3})(\b|$)";
|
public string? Name { get; set; }
|
||||||
private const string _validnessPattern = @"^1\d{6}[1|3]\d{3}$";
|
public decimal? Price { get; set; }
|
||||||
|
|
||||||
private static bool IsValudSku(string value)
|
private static bool IsValudSku(string value)
|
||||||
{
|
{
|
||||||
|
7
RhSolutions.SkuParser.Api/Models/ProductLine.cs
Normal file
7
RhSolutions.SkuParser.Api/Models/ProductLine.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace RhSolutions.SkuParser.Models;
|
||||||
|
|
||||||
|
public enum ProductLine
|
||||||
|
{
|
||||||
|
RAUTITAN,
|
||||||
|
RAUTHERMS
|
||||||
|
}
|
@ -1,11 +1,15 @@
|
|||||||
using RhSolutions.SkuParser.Abstractions;
|
using RhSolutions.SkuParser.Abstractions;
|
||||||
|
using RhSolutions.SkuParser.Api.Services;
|
||||||
using RhSolutions.SkuParser.Services;
|
using RhSolutions.SkuParser.Services;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
builder.Configuration
|
||||||
|
.AddJsonFile("appsettings.json");
|
||||||
builder.Services
|
builder.Services
|
||||||
.AddKeyedScoped<ISkuParser, CommonCsvParser>("text/csv")
|
.AddKeyedScoped<ISkuParser, CommonCsvParser>("text/csv")
|
||||||
.AddKeyedScoped<ISkuParser, CommonExcelParser>("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
.AddKeyedScoped<ISkuParser, CommonExcelParser>("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||||
.AddKeyedScoped<ISkuParser, CommonExcelParser>("application/vnd.ms-excel.sheet.macroenabled.12");
|
.AddKeyedScoped<ISkuParser, CommonExcelParser>("application/vnd.ms-excel.sheet.macroenabled.12")
|
||||||
|
.AddKeyedScoped<ISkuParser, BsExcelParser>("BS");
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
72
RhSolutions.SkuParser.Api/Services/BsExcelParser.cs
Normal file
72
RhSolutions.SkuParser.Api/Services/BsExcelParser.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using System.Data;
|
||||||
|
using ClosedXML.Excel;
|
||||||
|
using RhSolutions.SkuParser.Abstractions;
|
||||||
|
using RhSolutions.SkuParser.Models;
|
||||||
|
|
||||||
|
namespace RhSolutions.SkuParser.Api.Services;
|
||||||
|
|
||||||
|
public class BsExcelParser : ISkuParser
|
||||||
|
{
|
||||||
|
private IConfiguration configuration;
|
||||||
|
// private Dictionary<Product, double> result;
|
||||||
|
private const int rowsLookupCount = 20;
|
||||||
|
private const decimal vat = 1.2M;
|
||||||
|
public BsExcelParser(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
this.configuration = configuration;
|
||||||
|
// result = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<Product, double> ParseProducts(IFormFile file)
|
||||||
|
{
|
||||||
|
using XLWorkbook workbook = new(file.OpenReadStream());
|
||||||
|
IXLWorksheet ws = workbook.Worksheet(1);
|
||||||
|
var headers = configuration.GetSection("Headers");
|
||||||
|
ws.AutoFilter.Clear();
|
||||||
|
var cells = configuration.GetSection("Headers")
|
||||||
|
.GetChildren()
|
||||||
|
.ToDictionary(x => x.Path, x => ws.Range(1, 1, rowsLookupCount, ws.LastCellUsed().Address.ColumnNumber)
|
||||||
|
.Search(x.Value)
|
||||||
|
.FirstOrDefault())
|
||||||
|
.OrderBy(x => x.Value?.Address.ColumnNumber ?? int.MaxValue);
|
||||||
|
if (cells == null || cells.Any(x => x.Value == null))
|
||||||
|
{
|
||||||
|
throw new DataException($"В рабочей книге отсуствует таблица с необходимыми заголовками: {file.Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstCell = cells.First().Value;
|
||||||
|
var lastCell = cells.Last().Value?.WorksheetColumn().LastCellUsed();
|
||||||
|
var table = ws.Range(firstCell, lastCell).AsTable();
|
||||||
|
var rows = table.DataRange.Rows();
|
||||||
|
Dictionary<Product, double> result = new();
|
||||||
|
|
||||||
|
foreach (var row in rows.Where(r => r.Field(headers["Quantity"]).Value.IsNumber))
|
||||||
|
{
|
||||||
|
Product product = new()
|
||||||
|
{
|
||||||
|
Sku = row.Field(headers["Sku"]).GetString(),
|
||||||
|
ProductLine = row.Field(headers["ProductLine"]).GetString() switch
|
||||||
|
{
|
||||||
|
"RAUTITAN" => ProductLine.RAUTITAN,
|
||||||
|
"RAUTHERM S" => ProductLine.RAUTHERMS,
|
||||||
|
_ => null
|
||||||
|
},
|
||||||
|
Name = row.Field(headers["Name"]).GetString(),
|
||||||
|
Price = decimal.TryParse(row.Field(headers["Price"]).GetString(), out decimal value)
|
||||||
|
? Math.Round(value * vat, 2)
|
||||||
|
: null
|
||||||
|
};
|
||||||
|
|
||||||
|
double quantity = double.TryParse(row.Field(headers["Quantity"]).GetString(), out double q) ? q : 0;
|
||||||
|
if (result.ContainsKey(product))
|
||||||
|
{
|
||||||
|
result[product] += quantity;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Add(product, quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,5 +5,12 @@
|
|||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*",
|
||||||
|
"Headers": {
|
||||||
|
"ProductLine": "Программа",
|
||||||
|
"Sku": "Актуальный материал",
|
||||||
|
"Name": "Наименование",
|
||||||
|
"Quantity": "Кол-во",
|
||||||
|
"Price": "Цена брутто \nЕВРО\nбез НДС"
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user