| | 1 | | using Microsoft.Extensions.Logging; |
| | 2 | | using OfficeOpenXml; |
| | 3 | | using OfficeOpenXml.Style; |
| | 4 | | using OfficeOpenXml.Table; |
| | 5 | | using MRA.Infrastructure.Excel.Attributes; |
| | 6 | | using System.Drawing; |
| | 7 | | using MRA.Infrastructure.Exceptions.Excel; |
| | 8 | | using MRA.Infrastructure.Enums; |
| | 9 | |
|
| | 10 | | namespace MRA.Infrastructure.Excel; |
| | 11 | |
|
| | 12 | | public class EPPlusExcelProvider : IExcelProvider |
| | 13 | | { |
| | 14 | | private const string DICTIONARY_COLUMN_INDEX = "Index"; |
| | 15 | | private const string DICTIONARY_COLUMN_NAME = "Name"; |
| | 16 | |
|
| | 17 | | private readonly ILogger<EPPlusExcelProvider> _logger; |
| | 18 | |
|
| | 19 | |
|
| 1 | 20 | | public EPPlusExcelProvider(ILogger<EPPlusExcelProvider> logger) |
| | 21 | | { |
| 1 | 22 | | _logger = logger; |
| 1 | 23 | | ExcelPackage.License.SetNonCommercialPersonal("MiguelRomeral"); |
| 1 | 24 | | } |
| | 25 | |
|
| | 26 | | public List<ExcelColumnInfo> GetPropertiesAttributes<T>() |
| | 27 | | { |
| 0 | 28 | | _logger.LogTrace("Reading properties of type {Type}.", typeof(T)); |
| 0 | 29 | | return typeof(T) |
| 0 | 30 | | .GetProperties() |
| 0 | 31 | | .Where(p => Attribute.IsDefined(p, typeof(ExcelColumnAttribute))) |
| 0 | 32 | | .Select(p => new ExcelColumnInfo() |
| 0 | 33 | | { |
| 0 | 34 | | Property = p, |
| 0 | 35 | | Attribute = (ExcelColumnAttribute)Attribute.GetCustomAttribute(p, typeof(ExcelColumnAttribute)) |
| 0 | 36 | | }) |
| 0 | 37 | | .OrderBy(p => p.Attribute.Order) |
| 0 | 38 | | .ToList(); |
| | 39 | | } |
| | 40 | |
|
| | 41 | | public void SetTableHeaders(ref ExcelWorksheet workSheet, List<ExcelColumnInfo> properties) |
| | 42 | | { |
| 0 | 43 | | int col = 1; |
| 0 | 44 | | foreach (var prop in properties) |
| | 45 | | { |
| 0 | 46 | | workSheet.Cells[1, col].Value = prop.Attribute.Name; |
| 0 | 47 | | col++; |
| | 48 | | } |
| 0 | 49 | | } |
| | 50 | |
|
| | 51 | | public void CreateWorksheetDictionary(ExcelPackage excel, string sheetName, string tableName, Dictionary<int, string |
| | 52 | | List<ExcelColumnInfo> properties, ExcelWorksheet mainSheet, string nameColumnDropdown, string nameColumnIndex) |
| | 53 | | { |
| 0 | 54 | | var wsTypes = FillWorksheetDictionary(excel, name: sheetName, tableName: tableName, dictionary); |
| | 55 | |
|
| 0 | 56 | | AddDropdownColumn(mainSheet, wsTypes, tableName, |
| 0 | 57 | | dataRowStart: 2, |
| 0 | 58 | | dropdownColumn: FindColumnNumberOf(properties, nameColumnDropdown), |
| 0 | 59 | | indexColumn: FindColumnNumberOf(properties, nameColumnIndex)); |
| 0 | 60 | | } |
| | 61 | |
|
| | 62 | | public void CreateWorksheetDictionary<TEnum>(ExcelPackage excel, string sheetName, string tableName, List<ExcelColum |
| | 63 | | { |
| 0 | 64 | | var wsTypes = FillWorksheetDictionary<TEnum>(excel, name: sheetName, tableName: tableName); |
| | 65 | |
|
| 0 | 66 | | AddDropdownColumn(mainSheet, wsTypes, tableName, |
| 0 | 67 | | dataRowStart: 2, |
| 0 | 68 | | dropdownColumn: FindColumnNumberOf(properties, nameColumnDropdown), |
| 0 | 69 | | indexColumn: FindColumnNumberOf(properties, nameColumnIndex)); |
| 0 | 70 | | } |
| | 71 | |
|
| | 72 | | public int FindColumnNumberOf(List<ExcelColumnInfo> properties, string name) |
| | 73 | | { |
| 0 | 74 | | var index = properties.FindIndex(x => x.Attribute.Name.Equals(name)) + 1; |
| 0 | 75 | | if (index < 0) throw new ExcelColumnNotFoundException($"Column with name \"{name}\" was not found"); |
| 0 | 76 | | return index; |
| | 77 | | } |
| | 78 | |
|
| | 79 | | public ExcelWorksheet FillWorksheetDictionary(ExcelPackage excel, string name, string tableName, Dictionary<int, str |
| | 80 | | { |
| 0 | 81 | | var worksheet = excel.Workbook.Worksheets.Add(name); |
| | 82 | |
|
| 0 | 83 | | worksheet.Cells[1, 1].Value = DICTIONARY_COLUMN_NAME; |
| 0 | 84 | | worksheet.Cells[1, 2].Value = DICTIONARY_COLUMN_INDEX; |
| | 85 | |
|
| 0 | 86 | | int row = 1; |
| 0 | 87 | | foreach (var item in dictionary) |
| | 88 | | { |
| 0 | 89 | | row++; |
| 0 | 90 | | worksheet.Cells[row, 1].Value = item.Value; |
| 0 | 91 | | worksheet.Cells[row, 2].Value = item.Key; |
| 0 | 92 | | worksheet.Cells[row, 2].Style.Numberformat.Format = "#,##0"; |
| | 93 | | } |
| | 94 | |
|
| 0 | 95 | | CreateTable(ref worksheet, tableName, 1, 1, row, 2); |
| 0 | 96 | | worksheet.Column(1).Width = 60; |
| 0 | 97 | | return worksheet; |
| | 98 | | } |
| | 99 | |
|
| | 100 | | public ExcelWorksheet FillWorksheetDictionary<TEnum>(ExcelPackage excel, string name, string tableName) where TEnum |
| | 101 | | { |
| 0 | 102 | | var worksheet = excel.Workbook.Worksheets.Add(name); |
| | 103 | |
|
| 0 | 104 | | worksheet.Cells[1, 1].Value = DICTIONARY_COLUMN_NAME; |
| 0 | 105 | | worksheet.Cells[1, 2].Value = DICTIONARY_COLUMN_INDEX; |
| | 106 | |
|
| 0 | 107 | | int row = 1; |
| | 108 | |
|
| | 109 | | // Iterar sobre los valores del enum |
| 0 | 110 | | foreach (TEnum item in Enum.GetValues(typeof(TEnum))) |
| | 111 | | { |
| 0 | 112 | | row++; |
| 0 | 113 | | worksheet.Cells[row, 1].Value = item.GetDescription(); |
| 0 | 114 | | worksheet.Cells[row, 2].Value = Convert.ToInt32(item); |
| 0 | 115 | | worksheet.Cells[row, 2].Style.Numberformat.Format = "#,##0"; |
| | 116 | | } |
| | 117 | |
|
| 0 | 118 | | CreateTable(ref worksheet, tableName, 1, 1, row, 2); |
| 0 | 119 | | worksheet.Column(1).Width = 60; |
| 0 | 120 | | return worksheet; |
| | 121 | | } |
| | 122 | |
|
| | 123 | | private void AddDropdownColumn(ExcelWorksheet mainSheet, ExcelWorksheet dictionarySheet, string tableName, int dataR |
| | 124 | | { |
| | 125 | | // Obtener la tabla desde el worksheet del diccionario |
| 0 | 126 | | var dictionaryTable = dictionarySheet.Tables[tableName]; |
| 0 | 127 | | if (dictionaryTable == null) |
| 0 | 128 | | throw new ExcelTableNotFoundException($"La tabla '{tableName}' no fue encontrada en la hoja '{dictionaryShee |
| | 129 | |
|
| | 130 | | // Definir el nombre de rango dinámico para la columna "Name" en la tabla del diccionario |
| 0 | 131 | | string dynamicRangeName = $"{tableName}_NameRange"; |
| | 132 | |
|
| | 133 | | // Obtener la columna de "Name" en la tabla de diccionario |
| 0 | 134 | | int nameColumnIndex = dictionaryTable.Columns[DICTIONARY_COLUMN_NAME].Position + dictionaryTable.Address.Start.C |
| | 135 | |
|
| | 136 | | // Crear el rango dinámico usando un rango estructurado |
| 0 | 137 | | dictionarySheet.Workbook.Names.Add(dynamicRangeName, dictionarySheet.Cells[dictionaryTable.Address.Start.Row + 1 |
| | 138 | |
|
| | 139 | | // Agregar validación de lista en cada celda de la columna de dropdown en la hoja principal usando el nombre del |
| 0 | 140 | | for (int row = dataRowStart; row <= mainSheet.Dimension.End.Row; row++) |
| | 141 | | { |
| 0 | 142 | | var validation = mainSheet.DataValidations.AddListValidation(mainSheet.Cells[row, dropdownColumn].Address); |
| 0 | 143 | | validation.Formula.ExcelFormula = dynamicRangeName; |
| | 144 | |
|
| | 145 | | // Obtener la dirección de la celda del dropdown para referencia en VLOOKUP |
| 0 | 146 | | string dropdownCellAddress = mainSheet.Cells[row, dropdownColumn].Address; |
| | 147 | |
|
| | 148 | | // Asignar la fórmula VLOOKUP en cada celda de indexColumn |
| 0 | 149 | | mainSheet.Cells[row, indexColumn].Formula = $"VLOOKUP({dropdownCellAddress}, '{dictionarySheet.Name}'!A:B, 2 |
| | 150 | | } |
| 0 | 151 | | } |
| | 152 | |
|
| | 153 | |
|
| | 154 | | public void CreateTable(ref ExcelWorksheet workSheet, string name, int beginRow, int beginColumn, int endRow, int en |
| | 155 | | { |
| 0 | 156 | | var dataRange = workSheet.Cells[beginRow, beginColumn, endRow, endColumn]; |
| 0 | 157 | | var table = workSheet.Tables.Add(dataRange, name); |
| 0 | 158 | | table.TableStyle = TableStyles.Light6; |
| 0 | 159 | | StyleCellsHeader(ref workSheet, 1, 1, 1, endColumn); |
| 0 | 160 | | } |
| | 161 | |
|
| | 162 | | public void SetBold(ref ExcelWorksheet workSheet, int beginRow, int beginColumn, int endRow, int endColumn) |
| | 163 | | { |
| 0 | 164 | | using (var range = workSheet.Cells[beginRow, beginColumn, endRow, endColumn]) |
| | 165 | | { |
| 0 | 166 | | range.Style.Font.Bold = true; |
| 0 | 167 | | } |
| 0 | 168 | | } |
| | 169 | |
|
| | 170 | | public void StyleCellsHeader(ref ExcelWorksheet workSheet, int beginRow, int beginColumn, int endRow, int endColumn) |
| | 171 | | { |
| 0 | 172 | | using (var range = workSheet.Cells[beginRow, beginColumn, endRow, endColumn]) |
| | 173 | | { |
| 0 | 174 | | range.Style.Fill.PatternType = ExcelFillStyle.Solid; |
| 0 | 175 | | range.Style.Fill.BackgroundColor.SetColor(Color.RebeccaPurple); |
| 0 | 176 | | range.Style.Font.Color.SetColor(Color.White); |
| 0 | 177 | | range.Style.Font.Bold = true; |
| 0 | 178 | | } |
| 0 | 179 | | } |
| | 180 | |
|
| | 181 | | public Dictionary<string, int> GetColumnMapDrawing(ExcelWorksheet workSheet) |
| | 182 | | { |
| | 183 | | // Construir un mapeo entre los nombres de encabezado y la posición de columna |
| 0 | 184 | | var nameToColumnMap = new Dictionary<string, int>(); |
| 0 | 185 | | for (int col = 1; col <= workSheet.Dimension.End.Column; col++) |
| | 186 | | { |
| 0 | 187 | | var headerValue = workSheet.Cells[1, col].Text; |
| 0 | 188 | | if (!string.IsNullOrWhiteSpace(headerValue)) |
| | 189 | | { |
| 0 | 190 | | nameToColumnMap[headerValue] = col; |
| | 191 | | } |
| | 192 | | } |
| | 193 | |
|
| 0 | 194 | | return nameToColumnMap; |
| | 195 | | } |
| | 196 | |
|
| | 197 | | } |