using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.Rendering; using EveryThing.Data; using EveryThing.Models; using EveryThing.Models.Project; using Microsoft.AspNetCore.Http; using System.IO; using Microsoft.AspNetCore.Hosting; using ClosedXML.Excel; using EveryThing.Models.CodeTable; namespace EveryThing.Pages.Projects { [Authorize(Roles = "Administrator,ProjecThingUser")] public class CreatePartItemImportExcelModel : PageModel { private readonly IWebHostEnvironment _hostingEnvironment; private readonly ApplicationDbContext _context; private readonly UserManager _userManager; private readonly SignInManager _loginManager; private readonly RoleManager _roleManager; public CreatePartItemImportExcelModel(ApplicationDbContext context, UserManager userManager, SignInManager loginManager, RoleManager roleManager, IWebHostEnvironment environment) { _context = context; _userManager = userManager; _loginManager = loginManager; _roleManager = roleManager; _hostingEnvironment = environment; } [BindProperty] public int IdProject { get; set; } [BindProperty] public int IdProjectPart { get; set; } [BindProperty] public string FileName { get; set; } [BindProperty] public List ExcelItems { get; set; } [BindProperty] public string SelectedItems { get; set; } public IActionResult OnGet(int idProject, int idProjectPart) { IdProject = idProject; IdProjectPart = idProjectPart; ExcelItems = new List(); return Page(); } public async Task OnPostUpload(int idProject, int idProjectPart, List postedFiles) { if (postedFiles == null || postedFiles.Count != 1) { return new JsonResult(new { successful = false, error = "Izberite eno datoteko." }); } var path = Path.Combine(_hostingEnvironment.WebRootPath, "Uploads", "TempExcelImport"); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } else { //Pocistimo mapo foreach (var fileInfo in new DirectoryInfo(path).GetFiles("*.*")) { fileInfo.Delete(); } } var postedFile = postedFiles[0]; var fileName = postedFile.FileName; await using (var stream = new FileStream(Path.Combine(path, fileName), FileMode.Create)) { await postedFile.CopyToAsync(stream); } // Read excel headers var excelItems = new List(); var filePath = Path.Combine(path, fileName); var xlWorkbook = new XLWorkbook(filePath); var worksheet = xlWorkbook.Worksheet(1); int i = 1; if (!worksheet.Row(i).IsEmpty()) { IXLRow row = worksheet.Row(i); int j = 1; while (!row.Cell(j).IsEmpty()) { excelItems.Add(new ExcelItem { CellIndex = j, Name = row.Cell(j).Value.ToString(), }); j++; } } // Build mapping options var mappingOptions = typeof(Models.Project.ProjectPartItem).GetProperties() .Where(x => x.GetCustomAttributes(true).Length > 0 && x.GetCustomAttributes(true).Any(y => y.GetType() == typeof(System.ComponentModel.DataAnnotations.DisplayAttribute))) .Select(x => new { name = x.Name, display = ((System.ComponentModel.DataAnnotations.DisplayAttribute)x.GetCustomAttributes(true).First(y => y.GetType() == typeof(System.ComponentModel.DataAnnotations.DisplayAttribute))).Name }).ToList(); return new JsonResult(new { successful = true, fileName, excelItems, mappingOptions }); } public async Task OnPostImport(string selectedItems, string fileName, int idProjectPart, int idProject) { FileName = fileName; IdProjectPart = idProjectPart; IdProject = idProject; if (string.IsNullOrEmpty(selectedItems)) { return new JsonResult(new { successful = false, error = "Izberite vsaj eno polje." }); } var user = _userManager.GetUserAsync(User).Result; var excelItems = new List(); var items = selectedItems.Split('#'); foreach (var item in items) { var itemData = item.Split(';'); if (itemData.Length != 2) continue; excelItems.Add(new ExcelItem { CellIndex = Convert.ToInt32(itemData[0]), Name = itemData[1], }); } var path = Path.Combine(_hostingEnvironment.WebRootPath, "Uploads", "TempExcelImport", FileName); var xlWorkbook = new XLWorkbook(path); //ONLY FIRST LIST var worksheet = xlWorkbook.Worksheet(1); // Build display name lookup for properties var propertyDisplayNames = typeof(Models.Project.ProjectPartItem).GetProperties() .Where(x => x.GetCustomAttributes(true).Any(y => y is System.ComponentModel.DataAnnotations.DisplayAttribute)) .ToDictionary( x => x.Name, x => ((System.ComponentModel.DataAnnotations.DisplayAttribute)x.GetCustomAttributes(true).First(y => y is System.ComponentModel.DataAnnotations.DisplayAttribute)).Name ); // Validation pass - check all rows before importing var errors = new List(); int validationRow = 2; // Skip header while (!worksheet.Row(validationRow).IsEmpty()) { IXLRow row = worksheet.Row(validationRow); // Check if all mapped cells in the row are empty bool allEmpty = excelItems.All(ei => row.Cell(ei.CellIndex) == null || string.IsNullOrWhiteSpace(row.Cell(ei.CellIndex).Value.ToString())); if (allEmpty) { errors.Add(new { row = validationRow, property = "-", value = "", message = "Vsa polja v vrstici so prazna." }); validationRow++; continue; } foreach (var excelItem in excelItems) { if (row.Cell(excelItem.CellIndex) == null) continue; string value = row.Cell(excelItem.CellIndex).Value.ToString(); string displayName = propertyDisplayNames.ContainsKey(excelItem.Name) ? propertyDisplayNames[excelItem.Name] : excelItem.Name; // Empty individual cells are allowed - skip validation if (string.IsNullOrWhiteSpace(value)) continue; if (excelItem.Name is "IdItemFk" or "IdMaterialFk" or "IdMaterialSupplierFk") { // These are text lookups, no type conversion needed continue; } // Validate type conversion for other properties var propertyInfo = typeof(Models.Project.ProjectPartItem).GetProperties().FirstOrDefault(x => x.Name == excelItem.Name); if (propertyInfo != null) { try { var targetType = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType; Convert.ChangeType(value, targetType); } catch { errors.Add(new { row = validationRow, property = displayName, value, message = $"Ni mogoče pretvoriti v tip {propertyInfo.PropertyType.Name}." }); } } } validationRow++; } if (errors.Count > 0) { return new JsonResult(new { successful = false, validationErrors = errors }); } var insertedParts = _context.ProjectPartItems.Where(x => x.IdProjectPartFk == IdProjectPart); var currentPositionNumber = insertedParts.Any() ? insertedParts.Max(x => x.ProjectPartItemNumber) : 0; if (currentPositionNumber <= 0) currentPositionNumber = 0; int i = 2;//Skip header while (!worksheet.Row(i).IsEmpty()) { currentPositionNumber++; IXLRow row = worksheet.Row(i); var newPosition = new Models.Project.ProjectPartItem { IdProjectPartFk = IdProjectPart, NumberOfItems = 1, NumberOfSets = 1, IdMaterialSupplierFk = _context.CodeTablePartners.First().IdPartner, ProjectPartItemNumber = currentPositionNumber }; foreach (var excelItem in excelItems) { if (row.Cell(excelItem.CellIndex) == null) continue; string value = row.Cell(excelItem.CellIndex).Value.ToString(); if (string.IsNullOrWhiteSpace(value)) continue; if (excelItem.Name is "IdItemFk" or "IdMaterialFk") { var completableItem = _context.CodeTableItems.FirstOrDefault(x => x.Title == value && x.Active == true); if (completableItem == null) { //Add new if not exists completableItem = new Models.CodeTable.CodeTableItem { Active = true, IdCompanyFk = user.IdCompanyFk, Title = value, CodeTableItemType = excelItem.Name == "IdMaterialFk" ? CodeTableItemType.Material : CodeTableItemType.Product }; _context.CodeTableItems.Add(completableItem); await _context.SaveChangesAsync(); } if (excelItem.Name == "IdItemFk") newPosition.IdItemFk = completableItem.IdItem; else newPosition.IdMaterialFk = completableItem.IdItem; } else if (excelItem.Name is "IdMaterialSupplierFk") { var tmpPartner = _context.CodeTablePartners.FirstOrDefault(x => x.Title == value && x.Active == true); if (tmpPartner == null) { //Add new if not exists tmpPartner = new CodeTablePartner() { Active = true, IdCompanyFk = user.IdCompanyFk, Title = value, Supplier = true, IdCountryFk = _context.CodeTableCountries.First(x => x.Code == "SI").IdCountry, City = "", Street = "", HouseNumber = "", PostNumber = 0, Post = "", }; _context.CodeTablePartners.Add(tmpPartner); await _context.SaveChangesAsync(); } newPosition.IdMaterialSupplierFk = tmpPartner.IdPartner; } else { var propertyInfo = newPosition.GetType().GetProperties().First(x => x.Name == excelItem.Name); object propertyValue = Convert.ChangeType(value, Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType); propertyInfo.SetValue(newPosition, propertyValue); } } _context.ProjectPartItems.Add(newPosition); await _context.SaveChangesAsync(); i++; } System.IO.File.Delete(path); return new JsonResult(new { successful = true, redirectUrl = Url.Page("./Edit", new { id = IdProject }) }); } public class ExcelItem { public int CellIndex { get; set; } public string Name { get; set; } } } }