#if !NET472 using System.Runtime.Versioning; using RhSolutions.Tools; #endif using System.Text.RegularExpressions; namespace RhSolutions.Services; #if !NET472 [SupportedOSPlatform("windows")] #endif public class ExcelWriter : IWriter, IDisposable { private readonly Application _application; private Worksheet _worksheet; private readonly ResultBar _resultBar; private readonly Dictionary _headers; private readonly string _pricelistPath; private ProgressBar _progressBar; private Range _amountCell, _skuCell, _programLineCell, _nameCell, _oldSkuCell; public ExcelWriter(Application application, IAddInConfiguration configuration) { _application = application; _pricelistPath = configuration.GetPriceListPath(); _resultBar = new(); _headers = configuration.GetPriceListHeaders(); } public void WriteProducts(Dictionary products) { WriteProducts(new[] { (string.Empty, products) }); } public void WriteProducts(IEnumerable<(string, Dictionary)> products) { _worksheet = OpenNewPrice(); if (!_worksheet.IsValidSource()) { _application.ActiveWorkbook.Close(); throw new ArgumentException( $"Целевой файл {_application.ActiveWorkbook.Name} не является прайс-листом."); } _amountCell = _worksheet.Cells.Find(_headers["Amount"]); _skuCell = _worksheet.Cells.Find(_headers["Sku"]); _programLineCell = _worksheet.Cells.Find(_headers["ProductLine"]); _nameCell = _worksheet.Cells.Find(_headers["Name"]); _oldSkuCell = _worksheet.Cells.Find(_headers["OldSku"]); _progressBar = new("Заполняю строки...", products .Select(p => p.Item2) .Sum(set => set.Count)); if (products.Count() == 1) { foreach (var kvp in products.First().Item2) { FillPositionAmountToColumns(kvp, _amountCell.Column); _progressBar.Update(); } FilterByAmount(); _resultBar.Update(); } else { foreach (var product in products) { _worksheet.Columns[_amountCell.Column] .EntireColumn .Insert(XlInsertShiftDirection.xlShiftToRight, XlInsertFormatOrigin.xlFormatFromRightOrBelow); Range newColumnHeader = _worksheet.Cells[_amountCell.Row, _amountCell.Column - 1]; newColumnHeader.Value2 = $"{product.Item1}"; newColumnHeader.WrapText = true; foreach (var kvp in product.Item2) { FillPositionAmountToColumns(kvp, _amountCell.Column - 1, _amountCell.Column); _progressBar.Update(); } } FilterByAmount(); _resultBar.Update(); } } private Worksheet OpenNewPrice() { if (_application.Workbooks .Cast() .FirstOrDefault(w => w.FullName == _pricelistPath) != null) { throw new ArgumentException("Шаблонный файл редактируется в другом месте"); } return _application.Workbooks.Open(_pricelistPath, null, true).ActiveSheet; } private void FillPositionAmountToColumns(KeyValuePair positionAmount, params int[] columns) { Range worksheetCells = _worksheet.Cells; Range skuColumn = _skuCell.EntireColumn; int? row = GetPositionRow(skuColumn, positionAmount.Key.ProductSku.ToString(), positionAmount.Key.ProductLines.FirstOrDefault()); if (row != null) { foreach (int column in columns) { Range cell = worksheetCells[row, column]; cell.AddValue(positionAmount.Value); } _resultBar.IncrementSuccess(); return; } if (_oldSkuCell != null) { row = GetPositionRow(_oldSkuCell.EntireColumn, positionAmount.Key.ProductSku.ToString(), positionAmount.Key.ProductLines.FirstOrDefault()); if (row != null) { Range nameCell = worksheetCells[row, _nameCell.Column]; if (!Regex.IsMatch(nameCell.Value2, @"(арт. \d{11})")) { nameCell.AddValue($"-> замена (арт. {positionAmount.Key.ProductSku})"); } foreach (int column in columns) { Range cell = worksheetCells[row, column]; cell.AddValue(positionAmount.Value); } _resultBar.IncrementReplaced(); return; } } string sku = positionAmount.Key.ProductSku.Article; row = GetPositionRow(skuColumn, sku, positionAmount.Key.ProductLines.FirstOrDefault()); if (row != null) { Range nameCell = worksheetCells[row, _nameCell.Column]; if (!Regex.IsMatch(nameCell.Value2, @"(арт. \d{11})")) { nameCell.AddValue($" -> замена (арт. {positionAmount.Key.ProductSku})"); } foreach (int column in columns) { Range cell = worksheetCells[row, column]; cell.AddValue(positionAmount.Value); } _resultBar.IncrementReplaced(); return; } FillMissing(positionAmount, columns); _resultBar.IncrementNotFound(); } private void FillMissing(KeyValuePair positionAmount, params int[] columns) { Range worksheetCells = _worksheet.Cells; Range worksheetRows = _worksheet.Rows; int skuColumn = _skuCell.Column; int groupColumn = _programLineCell.Column; int nameColumn = _nameCell.Column; Product product = positionAmount.Key; int row = worksheetCells[worksheetRows.Count, skuColumn] .End[XlDirection.xlUp] .Row + 1; worksheetRows[row] .EntireRow .Insert(XlInsertShiftDirection.xlShiftDown, XlInsertFormatOrigin.xlFormatFromLeftOrAbove); Range previous = worksheetRows[row - 1]; Range current = worksheetRows[row]; previous.Copy(current); current.ClearContents(); worksheetCells[row, groupColumn].Value2 = product.ProductLines.FirstOrDefault() ?? string.Empty; worksheetCells[row, nameColumn].Value2 = $"{product.Name} -> не найден (арт. {product.ProductSku})"; worksheetCells[row, skuColumn].Value2 = "???"; if (_oldSkuCell != null) { worksheetCells[row, _oldSkuCell.Column].Value2 = product.ProductSku; } foreach (int column in columns) { Range cell = worksheetCells[row, column]; cell.AddValue(positionAmount.Value); } } private int? GetPositionRow(Range range, string sku, string group) { Range found = range.Find(sku); string foundGroupValue; if (found == null) { return null; } int firstFoundRow = found.Row; if (string.IsNullOrEmpty(group)) { return found.Row; } while (true) { foundGroupValue = _worksheet.Cells[found.Row, _programLineCell.Column].Value2.ToString(); if (group.Equals(foundGroupValue)) { return found.Row; } found = range.FindNext(found); if (found.Row == firstFoundRow) { return null; } } } private void FilterByAmount() { AutoFilter filter = _worksheet.AutoFilter; int startColumn = filter.Range.Column; filter.Range.AutoFilter(_amountCell.Column - startColumn + 1, "<>0", XlAutoFilterOperator.xlAnd, "<>"); _worksheet.Range["A1"].Activate(); } public void Dispose() { _progressBar?.Dispose(); _resultBar?.Dispose(); } }