using MailKit.Net.Smtp; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Manage.Internal; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.IdentityModel.Tokens; using MimeKit; using ODCWeb.Models.ViewModels; using System.Security.Claims; using ODCWeb.DataAccess.Data; using ODCWeb.DataAccess.Repository.IRepository; using ODCWeb.Models; using ODCWeb.Models.ViewModels; using ODCWeb.Services; using Org.BouncyCastle.Tls; using Org.BouncyCastle.Utilities.Net; using Syncfusion.EJ2.Inputs; using Syncfusion.XlsIO; using System.Data; using System.Diagnostics; using System.Dynamic; using System.Net; using System.Net.Mail; using System.Reflection; using System.Security.Claims; using System.Threading.Tasks; using System.Linq; namespace ODCWeb.Areas.Staff.Controllers { [Area("Staff")] public class ODCProjectController : Controller { private readonly ApplicationDbContext _db; private readonly IUnitOfWork _unitOfWork; private readonly UserManager userManager; private readonly IConstructionSheetReportService _reportService; private readonly ITemplateService _templateService; /* private readonly IConfiguration _configuration;*/ /* public ODCProjectController(IConfiguration configuration) { _configuration = configuration; }*/ public ODCProjectController(ApplicationDbContext applicationDbContext, UserManager userManager, IUnitOfWork unitOfWork, IConstructionSheetReportService reportService, ITemplateService templateService) { _db = applicationDbContext; _unitOfWork = unitOfWork; this.userManager = userManager; _reportService = reportService; _templateService = templateService; } [Authorize(Roles = "Admin,Employee,Customer")] public IActionResult Contractor_Dashboard() { return View(); } [Authorize(Roles = "Admin,Employee,Customer")] public IActionResult Quantity_Dashboard() { return View(); } //////////////////////////////////////////////////////////////////////////////// // ==================================================================================== // CONTRACTOR DASHBOARD - CREATE NEW PROJECT // ==================================================================================== [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult CreateNewProject() { var model = new ODCProject { StartDate = DateOnly.FromDateTime(DateTime.Today), EndDate = DateOnly.FromDateTime(DateTime.Today.AddMonths(6)) }; return View("Contractor_Dashboard/CreateNewProject", model); } [Authorize(Roles = "Admin,Employee")] [HttpPost] [ValidateAntiForgeryToken] public IActionResult CreateNewProject(ODCProject project) { // Get current user ID var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); project.UserId = userId; // Validate dates if (project.StartDate.HasValue && project.EndDate.HasValue) { if (project.EndDate < project.StartDate) { ModelState.AddModelError("EndDate", "End date must be after start date"); } } // Remove validation for optional fields ModelState.Remove("User"); ModelState.Remove("UserId"); ModelState.Remove("Type"); ModelState.Remove("Description"); ModelState.Remove("Client"); ModelState.Remove("StartDate"); ModelState.Remove("EndDate"); ModelState.Remove("P_Material_Cost"); if (ModelState.IsValid) { try { _unitOfWork.ODCProject.Add(project); _unitOfWork.save(); TempData["success"] = "Project created successfully!"; return RedirectToAction("Contractor_Dashboard"); } catch (Exception ex) { TempData["error"] = "Error creating project: " + ex.Message; return View("Contractor_Dashboard/CreateNewProject", project); } } // If we got this far, something failed var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage); TempData["error"] = "Please correct the errors: " + string.Join(", ", errors); return View("Contractor_Dashboard/CreateNewProject", project); } //////////////////////////////////////////////////////////////////////////////// // ==================================================================================== // CONTRACTOR DASHBOARD - VIEW ALL PROJECTS // ==================================================================================== [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult ViewAllProjects() { // Get current user ID var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); // Get all projects for the current user var projects = _unitOfWork.ODCProject .GetAll(includeProperties: "User") .Where(p => p.UserId == userId) .OrderByDescending(p => p.Id) .ToList(); return View("Contractor_Dashboard/ViewAllProjects", projects); } // Optional: Search/Filter functionality [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult ViewAllProjects(string searchTerm, string projectType, string sortBy) { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var projects = _unitOfWork.ODCProject .GetAll(includeProperties: "User") .Where(p => p.UserId == userId) .AsQueryable(); // Apply search filter if (!string.IsNullOrEmpty(searchTerm)) { projects = projects.Where(p => p.Name.Contains(searchTerm) || p.Location.Contains(searchTerm) || (p.Client != null && p.Client.Contains(searchTerm)) ); } // Apply type filter if (!string.IsNullOrEmpty(projectType) && projectType != "All") { projects = projects.Where(p => p.Type == projectType); } // Apply sorting projects = sortBy switch { "name_asc" => projects.OrderBy(p => p.Name), "name_desc" => projects.OrderByDescending(p => p.Name), "date_asc" => projects.OrderBy(p => p.StartDate), "date_desc" => projects.OrderByDescending(p => p.StartDate), "location" => projects.OrderBy(p => p.Location), _ => projects.OrderByDescending(p => p.Id) }; return View("Contractor_Dashboard/ViewAllProjects", projects.ToList()); } ///////////////////////////////////////////////////////////////////////////////// // ==================================================================================== // CONTRACTOR DASHBOARD - EDIT PROJECT DETAILS // ==================================================================================== [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult EditProjectDetails(int id) { var project = _unitOfWork.ODCProject.Get(u => u.Id == id); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("ViewAllProjects"); } // Verify ownership var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); if (project.UserId != userId) { TempData["error"] = "You don't have permission to edit this project"; return RedirectToAction("ViewAllProjects"); } return View("Contractor_Dashboard/EditProjectDetails", project); } [Authorize(Roles = "Admin,Employee")] [HttpPost] [ValidateAntiForgeryToken] public IActionResult EditProjectDetails(ODCProject project) { var existingProject = _unitOfWork.ODCProject.Get(u => u.Id == project.Id); if (existingProject == null) { TempData["error"] = "Project not found"; return RedirectToAction("ViewAllProjects"); } // Verify ownership var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); if (existingProject.UserId != userId) { TempData["error"] = "You don't have permission to edit this project"; return RedirectToAction("ViewAllProjects"); } // Validate dates if (project.StartDate.HasValue && project.EndDate.HasValue) { if (project.EndDate < project.StartDate) { ModelState.AddModelError("EndDate", "End date must be after start date"); } } // Remove validation for fields that shouldn't be validated ModelState.Remove("User"); ModelState.Remove("UserId"); ModelState.Remove("Type"); ModelState.Remove("Description"); ModelState.Remove("Client"); ModelState.Remove("StartDate"); ModelState.Remove("EndDate"); ModelState.Remove("P_Material_Cost"); if (ModelState.IsValid) { try { // Update only the editable fields existingProject.Name = project.Name; existingProject.Location = project.Location; existingProject.Type = project.Type; existingProject.Client = project.Client; existingProject.Description = project.Description; existingProject.StartDate = project.StartDate; existingProject.EndDate = project.EndDate; _unitOfWork.ODCProject.Update(existingProject); _unitOfWork.save(); TempData["success"] = "Project updated successfully!"; return RedirectToAction("ViewAllProjects"); } catch (Exception ex) { TempData["error"] = "Error updating project: " + ex.Message; return View("Contractor_Dashboard/EditProjectDetails", project); } } // If we got this far, something failed var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage); TempData["error"] = "Please correct the errors: " + string.Join(", ", errors); return View("Contractor_Dashboard/EditProjectDetails", project); } /// ///////////////////////////////////////////////////////////////////////////// // ==================================================================================== // ==================================================================================== // CONTRACTOR DASHBOARD - PROJECT SUMMARY (CORRECTED) // ==================================================================================== [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult ProjectSummaryView(int id) { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var project = _unitOfWork.ODCProject.Get( u => u.Id == id && u.UserId == userId, includeProperties: "User" ); if (project == null) { TempData["error"] = "Project not found or you don't have permission to view it"; return RedirectToAction("ViewAllProjects"); } // Get all floors for this project var floors = _unitOfWork.Floor.GetAll(includeProperties: "ODCProject") .Where(f => f.ODCProjectId == id) .ToList(); // Get all actions for these floors var floorIds = floors.Select(f => f.FloorId).ToList(); var actions = _unitOfWork.Action.GetAll(includeProperties: "Floor") .Where(a => floorIds.Contains(a.FloorId)) .ToList(); // Get all tasks for these actions var actionIds = actions.Select(a => a.ActionId).ToList(); var tasks = _unitOfWork.Task.GetAll() .Where(t => actionIds.Contains(t.ActionId)) .ToList(); // Create view model var viewModel = new ProjectSummaryyViewModel { Project = project, Floors = floors, Actions = actions, Tasks = tasks, // Calculate statistics TotalFloors = floors.Count, TotalActions = actions.Count, TotalTasks = tasks.Count, // Financial calculations TotalMaterialCost = tasks.Sum(t => t.Material_Cost ?? 0), TotalLaborCost = tasks.Sum(t => t.Labor_Cost ?? 0), TotalProjectedBudget = tasks.Sum(t => t.Mat_Lab ?? 0), // Modified/Actual calculations TotalModifiedLaborCost = tasks.Sum(t => t.M_Labor_Cost ?? 0), TotalModifiedMaterialCost = tasks.Sum(t => t.M_Mat_Cost ?? 0), TotalModifiedBudget = tasks.Sum(t => t.M_Budget ?? 0), TotalActualLaborCost = tasks.Sum(t => t.A_Labor_Cost ?? 0), TotalActualMaterialCost = tasks.Sum(t => t.A_Mat_Cost ?? 0), TotalBudgetLeft = tasks.Sum(t => t.A_Budget_Left ?? 0), TotalProfitLoss = tasks.Sum(t => t.T_Profit_Loss ?? 0), // Completion statistics AverageCompletion = tasks.Any() ? tasks.Average(t => t.Completion ?? 0) : 0, CompletedTasks = tasks.Count(t => (t.Completion ?? 0) >= 100), InProgressTasks = tasks.Count(t => (t.Completion ?? 0) > 0 && (t.Completion ?? 0) < 100), NotStartedTasks = tasks.Count(t => (t.Completion ?? 0) == 0), // Date calculations EarliestStartDate = tasks.Where(t => t.SDate.HasValue).Any() ? tasks.Where(t => t.SDate.HasValue).Min(t => t.SDate) : null, LatestEndDate = CalculateLatestEndDate(tasks) }; return View("Contractor_Dashboard/ProjectSummaryView", viewModel); } // Helper method to calculate latest end date private DateOnly? CalculateLatestEndDate(List tasks) { DateOnly? latestDate = null; foreach (var task in tasks.Where(t => t.SDate.HasValue && t.Duration.HasValue)) { var endDate = task.SDate.Value.AddDays(task.Duration.Value); if (latestDate == null || endDate > latestDate) { latestDate = endDate; } } return latestDate; } // Project Summary Selection (if accessed from dashboard button) [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult ProjectSummarySelection() { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var projects = _unitOfWork.ODCProject.GetAll() .Where(p => p.UserId == userId) .Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }) .ToList(); ViewBag.Projects = projects; return View("Contractor_Dashboard/ProjectSummarySelection"); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult ProjectSummarySelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("ProjectSummarySelection"); } return RedirectToAction("ProjectSummaryView", new { id = projectId }); } /// ///////////////////////////////////////////////////////////////////////////// // ==================================================================================== // CONTRACTOR DASHBOARD - VIEW TASK VS MATERIAL // ==================================================================================== [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult ViewTaskVsMaterial(int id) { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var project = _unitOfWork.ODCProject.Get( u => u.Id == id && u.UserId == userId, includeProperties: "User" ); if (project == null) { TempData["error"] = "Project not found or you don't have permission to view it"; return RedirectToAction("ViewAllProjects"); } // Get all floors for this project var floors = _unitOfWork.Floor.GetAll(includeProperties: "ODCProject") .Where(f => f.ODCProjectId == id) .ToList(); // Get all actions for these floors var allActions = _unitOfWork.Action.GetAll(includeProperties: "Floor").ToList(); var floorIds = floors.Select(f => f.FloorId).ToList(); var actions = allActions.Where(a => floorIds.Contains(a.FloorId)).ToList(); // Get all tasks for these actions var allTasks = _unitOfWork.Task.GetAll().ToList(); var actionIds = actions.Select(a => a.ActionId).ToList(); var tasks = allTasks.Where(t => actionIds.Contains(t.ActionId)).ToList(); // Get all materials for these tasks var taskIds = tasks.Select(t => t.TaskId).ToList(); var materials = _unitOfWork.Material.GetAll() .Where(m => taskIds.Contains((int)m.TaskId)) .ToList(); // Group materials by task var taskMaterialGroups = materials.GroupBy(m => m.TaskId).ToList(); // Create view model with task-material mapping var viewModel = new TaskVsMaterialViewModel { Project = project, Floors = floors, Actions = actions, Tasks = tasks, Materials = materials, // Create task details list TaskDetails = tasks.Select(task => { var taskMaterials = materials.Where(m => m.TaskId == task.TaskId).ToList(); var action = actions.FirstOrDefault(a => a.ActionId == task.ActionId); var floor = action != null ? floors.FirstOrDefault(f => f.FloorId == action.FloorId) : null; return new TaskMaterialDetail { Task = task, FloorName = floor?.Name ?? "Unknown", ActionName = action?.EffectiveDisplayNameA ?? "Unknown", Materials = taskMaterials, MaterialCount = taskMaterials.Count, TotalMaterialCost = taskMaterials.Sum(m => (m.Qty) * (m.Unit_Cost ?? 0)), TotalPlannedQuantity = taskMaterials.Sum(m => m.Qty), TotalActualQuantity = taskMaterials.Sum(m => m.Qty), HasMaterials = taskMaterials.Any() }; }).ToList(), // Summary statistics TotalTasks = tasks.Count, TasksWithMaterials = tasks.Count(t => materials.Any(m => m.TaskId == t.TaskId)), TasksWithoutMaterials = tasks.Count(t => !materials.Any(m => m.TaskId == t.TaskId)), TotalMaterials = materials.Count, TotalMaterialCost = materials.Sum(m => (m.Qty) * (m.Unit_Cost ?? 0)), TotalActualMaterialCost = materials.Sum(m => (m.Qty) * (m.ActualCost ?? 0)) }; return View("Contractor_Dashboard/ViewTaskVsMaterial", viewModel); } // Task vs Material Selection [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult ViewTaskVsMaterialSelection() { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var projects = _unitOfWork.ODCProject.GetAll() .Where(p => p.UserId == userId) .Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }) .ToList(); ViewBag.Projects = projects; return View("Contractor_Dashboard/ViewTaskVsMaterialSelection"); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult ViewTaskVsMaterialSelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("ViewTaskVsMaterialSelection"); } return RedirectToAction("ViewTaskVsMaterial", new { id = projectId }); } ///////////////////////////////////////////////////////////////////////////////// // ==================================================================================== // CONTRACTOR DASHBOARD - GENERATE BQ // ==================================================================================== [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult GenerateBQSelection() { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); // Get user's projects var projects = _unitOfWork.ODCProject.GetAll() .Where(p => p.UserId == userId) .Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }) .ToList(); // Get active BQ templates var templates = _unitOfWork.StandaloneBQTemplate .GetAll() .Where(t => t.IsTemplateActive == true) .Select(t => new SelectListItem { Text = t.TemplateName, Value = t.StandaloneBQTemplateId.ToString() }) .ToList(); ViewBag.Projects = projects; ViewBag.Templates = templates; return View("Contractor_Dashboard/GenerateBQSelection"); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult GenerateBQSelection(int projectId, int templateId, string unitSystem = "metric") { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("GenerateBQSelection"); } if (templateId == 0) { TempData["error"] = "Please select a BQ template"; return RedirectToAction("GenerateBQSelection"); } // Verify project ownership var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var project = _unitOfWork.ODCProject.Get(u => u.Id == projectId); if (project == null || project.UserId != userId) { TempData["error"] = "Project not found or you don't have permission to access it"; return RedirectToAction("GenerateBQSelection"); } // Redirect to BQGeneration controller return RedirectToAction("ExportBQToExcel", "BQGeneration", new { area = "Admin", projectId = projectId, templateId = templateId, unitSystem = unitSystem }); } // Alternative: Direct BQ Generation View (if you want preview before download) [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult GenerateBQPreview(int projectId, int templateId, string unitSystem = "metric") { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var project = _unitOfWork.ODCProject.Get( u => u.Id == projectId && u.UserId == userId, includeProperties: "User" ); if (project == null) { TempData["error"] = "Project not found or you don't have permission to view it"; return RedirectToAction("GenerateBQSelection"); } var template = _unitOfWork.StandaloneBQTemplate.Get(t => t.StandaloneBQTemplateId == templateId); if (template == null) { TempData["error"] = "BQ Template not found"; return RedirectToAction("GenerateBQSelection"); } var viewModel = new GenerateBQViewModel { Project = project, Template = template, UnitSystem = unitSystem, ProjectId = projectId, TemplateId = templateId }; return View("Contractor_Dashboard/GenerateBQPreview", viewModel); } /// ///////////////////////////////////////////////////////////////////////////// // ==================================================================================== // CONTRACTOR DASHBOARD - MATERIAL LISTS // ==================================================================================== [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult MaterialLists(int id) { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var project = _unitOfWork.ODCProject.Get( u => u.Id == id && u.UserId == userId, includeProperties: "User" ); if (project == null) { TempData["error"] = "Project not found or you don't have permission to view it"; return RedirectToAction("ViewAllProjects"); } // Get all floors for this project var floors = _unitOfWork.Floor.GetAll(includeProperties: "ODCProject") .Where(f => f.ODCProjectId == id) .ToList(); // Get all actions for these floors var allActions = _unitOfWork.Action.GetAll(includeProperties: "Floor").ToList(); var floorIds = floors.Select(f => f.FloorId).ToList(); var actions = allActions.Where(a => floorIds.Contains(a.FloorId)).ToList(); // Get all tasks for these actions var allTasks = _unitOfWork.Task.GetAll().ToList(); var actionIds = actions.Select(a => a.ActionId).ToList(); var tasks = allTasks.Where(t => actionIds.Contains(t.ActionId)).ToList(); // Get all materials for these tasks var taskIds = tasks.Select(t => t.TaskId).ToList(); var materials = _unitOfWork.Material.GetAll() .Where(m => taskIds.Contains((int)m.TaskId)) .ToList(); // Create material list view model var viewModel = new MaterialListtViewModel { Project = project, Floors = floors, Actions = actions, Tasks = tasks, Materials = materials, // Group materials by category/type MaterialsByCategory = GroupMaterialsByCategoryy(materials), // Create detailed material summary MaterialSummary = CreateMaterialSummary(materials, tasks, actions, floors), // Statistics TotalMaterialTypes = materials.Select(m => m.Name).Distinct().Count(), TotalMaterialItems = materials.Count, TotalPlannedCost = materials.Sum(m => (m.Qty) * (m.Unit_Cost ?? 0)), TotalActualCost = materials.Sum(m => (m.Actual_Qty ?? 0) * (m.ActualCost ?? 0)), TasksWithMaterials = tasks.Count(t => materials.Any(m => m.TaskId == t.TaskId)), TasksWithoutMaterials = tasks.Count(t => !materials.Any(m => m.TaskId == t.TaskId)) }; return View("Contractor_Dashboard/MaterialLists", viewModel); } // Material Lists Selection [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult MaterialListsSelection() { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var projects = _unitOfWork.ODCProject.GetAll() .Where(p => p.UserId == userId) .Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }) .ToList(); ViewBag.Projects = projects; return View("Contractor_Dashboard/MaterialListsSelection"); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult MaterialListsSelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("MaterialListsSelection"); } return RedirectToAction("MaterialLists", new { id = projectId }); } // Helper method to group materials by category private Dictionary> GroupMaterialsByCategoryy(List materials) { var categorized = new Dictionary>(); foreach (var material in materials) { string category = DetermineMaterialCategory(material.Name, material.Unit); if (!categorized.ContainsKey(category)) { categorized[category] = new List(); } categorized[category].Add(material); } return categorized; } // Helper method to determine material category private string DetermineMaterialCategory(string materialName, string unit) { if (string.IsNullOrEmpty(materialName)) return "Uncategorized"; string name = materialName.ToLower(); string unitLower = unit?.ToLower() ?? ""; // Concrete and aggregates if (name.Contains("concrete") || name.Contains("cement") || name.Contains("sand") || name.Contains("aggregate") || name.Contains("gravel") || name.Contains("marl")) return "Concrete & Aggregates"; // Steel and reinforcement if (name.Contains("steel") || name.Contains("rod") || name.Contains("bar") || name.Contains("reinforcement") || name.Contains("mesh") || name.Contains("wire") || unitLower.Contains("kg")) return "Steel & Reinforcement"; // Bricks and blocks if (name.Contains("brick") || name.Contains("block") || name.Contains("stone")) return "Bricks & Blocks"; // Wood and timber if (name.Contains("wood") || name.Contains("timber") || name.Contains("plywood") || name.Contains("board")) return "Wood & Timber"; // Paint and finishes if (name.Contains("paint") || name.Contains("varnish") || name.Contains("stain") || name.Contains("coating")) return "Paint & Finishes"; // Tiles and flooring if (name.Contains("tile") || name.Contains("floor") || name.Contains("carpet")) return "Tiles & Flooring"; // Plumbing materials if (name.Contains("pipe") || name.Contains("plumb") || name.Contains("valve") || name.Contains("fitting") || name.Contains("tap")) return "Plumbing"; // Electrical materials if (name.Contains("wire") || name.Contains("cable") || name.Contains("switch") || name.Contains("socket") || name.Contains("electrical")) return "Electrical"; // Hardware and fixtures if (name.Contains("nail") || name.Contains("screw") || name.Contains("bolt") || name.Contains("hinge") || name.Contains("lock") || name.Contains("handle")) return "Hardware & Fixtures"; // Waterproofing and insulation if (name.Contains("waterproof") || name.Contains("insulation") || name.Contains("damp")) return "Waterproofing & Insulation"; return "Other Materials"; } // Helper method to create material summary private List CreateMaterialSummary(List materials, List tasks, List actions, List floors) { var summary = new List(); // Group by material name and unit var groupedMaterials = materials .GroupBy(m => new { m.Name, m.Unit }) .OrderBy(g => g.Key.Name); foreach (var group in groupedMaterials) { var totalPlanned = group.Sum(m => m.Qty); var totalActual = group.Sum(m => m.Actual_Qty ?? 0); var averageRate = group.Average(m => m.Unit_Cost ?? 0); var totalCost = group.Sum(m => (m.Qty) * (m.Unit_Cost ?? 0)); var actualCost = group.Sum(m => (m.Actual_Qty ?? 0) * (m.ActualCost ?? 0)); var taskLocations = new List(); foreach (var material in group) { var task = tasks.FirstOrDefault(t => t.TaskId == material.TaskId); if (task != null) { var action = actions.FirstOrDefault(a => a.ActionId == task.ActionId); if (action != null) { var floor = floors.FirstOrDefault(f => f.FloorId == action.FloorId); if (floor != null) { taskLocations.Add($"{floor.Name} - {action.EffectiveDisplayNameA} - {task.EffectiveDisplayName}"); } } } } summary.Add(new MaterialSummaryItem { MaterialName = group.Key.Name, Unit = group.Key.Unit, TotalPlannedQuantity = totalPlanned, TotalActualQuantity = totalActual, AverageRate = averageRate, TotalPlannedCost = totalCost, TotalActualCost = actualCost, Variance = totalActual - totalPlanned, VariancePercentage = totalPlanned > 0 ? ((totalActual - totalPlanned) / totalPlanned) * 100 : 0, CostVariance = actualCost - totalCost, UsageCount = group.Count(), Locations = taskLocations, Category = DetermineMaterialCategory(group.Key.Name, group.Key.Unit) }); } return summary; } /// ///////////////////////////////////////////////////////////////////////////// // ==================================================================================== // CONTRACTOR DASHBOARD - EXPORT MATERIAL REPORT // ==================================================================================== [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult ExportMaterialReport(int id, string format = "excel") { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var project = _unitOfWork.ODCProject.Get( u => u.Id == id && u.UserId == userId, includeProperties: "User" ); if (project == null) { TempData["error"] = "Project not found or you don't have permission to access it"; return RedirectToAction("ViewAllProjects"); } // Get all project data var floors = _unitOfWork.Floor.GetAll(includeProperties: "ODCProject") .Where(f => f.ODCProjectId == id) .ToList(); var allActions = _unitOfWork.Action.GetAll(includeProperties: "Floor").ToList(); var floorIds = floors.Select(f => f.FloorId).ToList(); var actions = allActions.Where(a => floorIds.Contains(a.FloorId)).ToList(); var allTasks = _unitOfWork.Task.GetAll().ToList(); var actionIds = actions.Select(a => a.ActionId).ToList(); var tasks = allTasks.Where(t => actionIds.Contains(t.ActionId)).ToList(); var taskIds = tasks.Select(t => t.TaskId).ToList(); var materials = _unitOfWork.Material.GetAll() .Where(m => taskIds.Contains((int)m.TaskId)) .ToList(); if (!materials.Any()) { TempData["error"] = "No materials found for this project"; return RedirectToAction("MaterialLists", new { id = id }); } // Generate report based on format byte[] reportFile; string fileName; string contentType; if (format.ToLower() == "pdf") { reportFile = GenerateMaterialPdfReport(project, materials, tasks, actions, floors); fileName = $"Material_Report_{project.Name.Replace(" ", "_")}_{DateTime.Now:yyyyMMdd_HHmmss}.pdf"; contentType = "application/pdf"; } else // Default to Excel { reportFile = GenerateMaterialExcelReport(project, materials, tasks, actions, floors); fileName = $"Material_Report_{project.Name.Replace(" ", "_")}_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx"; contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; } return File(reportFile, contentType, fileName); } // Export Material Report Selection [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult ExportMaterialReportSelection() { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var projects = _unitOfWork.ODCProject.GetAll() .Where(p => p.UserId == userId) .Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }) .ToList(); ViewBag.Projects = projects; return View("Contractor_Dashboard/ExportMaterialReportSelection"); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult ExportMaterialReportSelection(int projectId, string format = "excel") { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("ExportMaterialReportSelection"); } return RedirectToAction("ExportMaterialReport", new { id = projectId, format = format }); } // Helper method to generate Excel report private byte[] GenerateMaterialExcelReport(ODCProject project, List materials, List tasks, List actions, List floors) { using (ExcelEngine excelEngine = new ExcelEngine()) { IApplication application = excelEngine.Excel; application.DefaultVersion = ExcelVersion.Excel2016; IWorkbook workbook = application.Workbooks.Create(1); IWorksheet worksheet = workbook.Worksheets[0]; worksheet.Name = "Material Report"; // Set global vertical alignment worksheet.Range.CellStyle.VerticalAlignment = ExcelVAlign.VAlignCenter; int row = 1; // Header var headerRange = worksheet.Range[row, 1, row + 1, 10]; headerRange.Merge(); headerRange.Text = $"MATERIAL REPORT\n{project.Name} - {project.Location}"; headerRange.CellStyle.Font.Bold = true; headerRange.CellStyle.Font.Size = 16; headerRange.CellStyle.HorizontalAlignment = ExcelHAlign.HAlignCenter; // headerRange.CellStyle.Color = Color.FromArgb(245, 245, 245); headerRange.CellStyle.WrapText = true; worksheet.SetRowHeight(row, 50); row += 3; // Report Info worksheet.Range[row, 1].Text = $"Generated: {DateTime.Now:dd/MM/yyyy HH:mm}"; worksheet.Range[row, 1].CellStyle.Font.Size = 10; worksheet.Range[row, 6].Text = $"Total Materials: {materials.Count}"; worksheet.Range[row, 6].CellStyle.Font.Bold = true; row += 2; // Summary Statistics var summaryRange = worksheet.Range[row, 1, row, 10]; // summaryRange.CellStyle.Color = Color.FromArgb(251, 191, 36); summaryRange.CellStyle.Font.Bold = true; summaryRange.CellStyle.Font.Color = ExcelKnownColors.White; worksheet.Range[row, 1, row, 2].Merge(); worksheet.Range[row, 1].Text = "SUMMARY"; worksheet.Range[row, 1].CellStyle.Font.Size = 12; row++; double totalPlannedCost = materials.Sum(m => (m.Qty) * (m.Unit_Cost ?? 0)); double totalActualCost = materials.Sum(m => (m.Actual_Qty ?? 0) * (m.ActualCost ?? 0)); double variance = totalActualCost - totalPlannedCost; worksheet.Range[row, 1].Text = "Total Planned Cost:"; worksheet.Range[row, 2].Number = totalPlannedCost; worksheet.Range[row, 2].NumberFormat = "#,##0.00"; worksheet.Range[row, 4].Text = "Total Actual Cost:"; worksheet.Range[row, 5].Number = totalActualCost; worksheet.Range[row, 5].NumberFormat = "#,##0.00"; worksheet.Range[row, 7].Text = "Variance:"; worksheet.Range[row, 8].Number = variance; worksheet.Range[row, 8].NumberFormat = "#,##0.00"; worksheet.Range[row, 8].CellStyle.Font.Color = variance >= 0 ? ExcelKnownColors.Red : ExcelKnownColors.Green; row += 2; // Table Headers string[] headers = new[] { "Material", "Category", "Unit", "Planned Qty", "Rate (PKR)", "Planned Cost", "Actual Qty", "Actual Cost", "Variance", "Location" }; var headerRowRange = worksheet.Range[row, 1, row, headers.Length]; // headerRowRange.CellStyle.Color = Color.FromArgb(128, 128, 128); headerRowRange.CellStyle.Font.Bold = true; headerRowRange.CellStyle.Font.Color = ExcelKnownColors.White; for (int i = 0; i < headers.Length; i++) { worksheet.Range[row, i + 1].Text = headers[i]; } worksheet.SetRowHeight(row, 25); row++; // Group materials by category var materialsByCategory = materials .GroupBy(m => DetermineMaterialCategory(m.Name, m.Unit)) .OrderBy(g => g.Key); foreach (var categoryGroup in materialsByCategory) { // Category header var categoryRange = worksheet.Range[row, 1, row, headers.Length]; categoryRange.Merge(); categoryRange.Text = categoryGroup.Key.ToUpper(); categoryRange.CellStyle.Font.Bold = true; categoryRange.CellStyle.Font.Size = 11; // categoryRange.CellStyle.Color = Color.FromArgb(255, 251, 235); worksheet.SetRowHeight(row, 20); row++; // Materials in category foreach (var material in categoryGroup.OrderBy(m => m.Name)) { var task = tasks.FirstOrDefault(t => t.TaskId == material.TaskId); var action = task != null ? actions.FirstOrDefault(a => a.ActionId == task.ActionId) : null; var floor = action != null ? floors.FirstOrDefault(f => f.FloorId == action.FloorId) : null; string location = floor != null && action != null && task != null ? $"{floor.Name} > {action.EffectiveDisplayNameA} > {task.EffectiveDisplayName}" : "N/A"; double plannedQty = material.Qty; double rate = material.Unit_Cost ?? 0; double plannedCost = plannedQty * rate; double actualQty = material.Actual_Qty ?? 0; double actualCost = actualQty * rate; double qtyVariance = actualQty - plannedQty; worksheet.Range[row, 1].Text = material.Name; worksheet.Range[row, 2].Text = categoryGroup.Key; worksheet.Range[row, 3].Text = material.Unit; worksheet.Range[row, 4].Number = plannedQty; worksheet.Range[row, 5].Number = rate; worksheet.Range[row, 6].Number = plannedCost; worksheet.Range[row, 7].Number = actualQty; worksheet.Range[row, 8].Number = actualCost; worksheet.Range[row, 9].Number = qtyVariance; worksheet.Range[row, 10].Text = location; // Formatting worksheet.Range[row, 4].NumberFormat = "#,##0.00"; worksheet.Range[row, 5].NumberFormat = "#,##0.00"; worksheet.Range[row, 6].NumberFormat = "#,##0.00"; worksheet.Range[row, 7].NumberFormat = "#,##0.00"; worksheet.Range[row, 8].NumberFormat = "#,##0.00"; worksheet.Range[row, 9].NumberFormat = "#,##0.00"; // Color code variance if (qtyVariance > 0) worksheet.Range[row, 9].CellStyle.Font.Color = ExcelKnownColors.Red; else if (qtyVariance < 0) worksheet.Range[row, 9].CellStyle.Font.Color = ExcelKnownColors.Green; // Alternating row colors if (row % 2 == 0) // worksheet.Range[row, 1, row, headers.Length].CellStyle.Color = Color.FromArgb(250, 250, 250); worksheet.Range[row, 10].CellStyle.WrapText = true; worksheet.SetRowHeight(row, 20); row++; } row++; // Space between categories } // Column widths worksheet.SetColumnWidth(1, 25); // Material worksheet.SetColumnWidth(2, 20); // Category worksheet.SetColumnWidth(3, 10); // Unit worksheet.SetColumnWidth(4, 12); // Planned Qty worksheet.SetColumnWidth(5, 12); // Rate worksheet.SetColumnWidth(6, 15); // Planned Cost worksheet.SetColumnWidth(7, 12); // Actual Qty worksheet.SetColumnWidth(8, 15); // Actual Cost worksheet.SetColumnWidth(9, 12); // Variance worksheet.SetColumnWidth(10, 40); // Location // Page setup worksheet.PageSetup.Orientation = ExcelPageOrientation.Landscape; worksheet.PageSetup.PaperSize = ExcelPaperSize.PaperA4; worksheet.PageSetup.FitToPagesWide = 1; worksheet.PageSetup.FitToPagesTall = 0; // Save to memory stream MemoryStream stream = new MemoryStream(); workbook.SaveAs(stream); stream.Position = 0; byte[] result = stream.ToArray(); stream.Close(); return result; } } // Helper method to generate PDF report (placeholder) private byte[] GenerateMaterialPdfReport(ODCProject project, List materials, List tasks, List actions, List floors) { // For now, generate Excel and return it as PDF would require additional library // You can implement actual PDF generation using a library like iTextSharp or similar return GenerateMaterialExcelReport(project, materials, tasks, actions, floors); } ///////////////////////////////////////////////////////////////////////////////// // QS Dashboard - Export BQ to Excel [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult ExportBQSelection() { // Get list of projects var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); // Get list of active BQ templates var templates = _unitOfWork.StandaloneBQTemplate.Where(t => t.IsTemplateActive == true) .Select(t => new SelectListItem { Text = t.TemplateName, Value = t.StandaloneBQTemplateId.ToString() }); ViewBag.Projects = projects; ViewBag.Templates = templates; return View(); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult ExportBQSelection(int projectId, int templateId, string unitSystem = "metric") { if (projectId == 0 || templateId == 0) { TempData["error"] = "Please select both project and template"; return RedirectToAction("ExportBQSelection"); } // Redirect to BQGeneration controller's export method return RedirectToAction("ExportBQToExcel", "BQGeneration", new { area = "Admin", projectId = projectId, templateId = templateId, unitSystem = unitSystem }); } // ==================================================================================== // CONTRACTOR DASHBOARD - GANTT CHART VIEW // ==================================================================================== [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult GanttChartView(int id) { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var project = _unitOfWork.ODCProject.Get( u => u.Id == id && u.UserId == userId, includeProperties: "User" ); if (project == null) { TempData["error"] = "Project not found or you don't have permission to view it"; return RedirectToAction("ViewAllProjects"); } // Get all floors for this project var floors = _unitOfWork.Floor.GetAll(includeProperties: "ODCProject") .Where(f => f.ODCProjectId == id) .ToList(); // Get all actions for these floors var allActions = _unitOfWork.Action.GetAll(includeProperties: "Floor").ToList(); var floorIds = floors.Select(f => f.FloorId).ToList(); var actions = allActions.Where(a => floorIds.Contains(a.FloorId)).ToList(); // Get all tasks for these actions var allTasks = _unitOfWork.Task.GetAll().ToList(); var actionIds = actions.Select(a => a.ActionId).ToList(); var tasks = allTasks.Where(t => actionIds.Contains(t.ActionId)).ToList(); // Create Gantt chart data var ganttData = new List(); int taskOrder = 1; foreach (var floor in floors.OrderBy(f => f.Name)) { // Add floor as parent task var floorStartDate = DateTime.Today; var floorEndDate = DateTime.Today.AddDays(30); var floorActions = actions.Where(a => a.FloorId == floor.FloorId).OrderBy(a => a.Name).ToList(); if (floorActions.Any()) { var floorTasks = new List(); foreach (var action in floorActions) { floorTasks.AddRange(tasks.Where(t => t.ActionId == action.ActionId)); } if (floorTasks.Any(t => t.SDate.HasValue)) { var minDate = floorTasks.Where(t => t.SDate.HasValue).Min(t => t.SDate!.Value); floorStartDate = minDate.ToDateTime(TimeOnly.MinValue); var maxEndDate = floorTasks .Where(t => t.SDate.HasValue && t.Duration.HasValue) .Select(t => t.SDate!.Value.AddDays(t.Duration!.Value)) .DefaultIfEmpty(minDate) .Max(); floorEndDate = maxEndDate.ToDateTime(TimeOnly.MinValue); } } ganttData.Add(new GanttTaskViewModel { TaskId = taskOrder++, TaskName = floor.Name, StartDate = floorStartDate, EndDate = floorEndDate, Duration = (floorEndDate - floorStartDate).Days, Progress = CalculateFloorProgress(floor.FloorId, actions, tasks), ParentId = null, IsParent = true, Level = 0 }); int floorTaskId = taskOrder - 1; foreach (var action in floorActions) { var actionTasks = tasks.Where(t => t.ActionId == action.ActionId).OrderBy(t => t.Name).ToList(); if (!actionTasks.Any()) continue; // Calculate action dates var actionStartDate = DateTime.Today; var actionEndDate = DateTime.Today.AddDays(7); if (actionTasks.Any(t => t.SDate.HasValue)) { var minDate = actionTasks.Where(t => t.SDate.HasValue).Min(t => t.SDate!.Value); actionStartDate = minDate.ToDateTime(TimeOnly.MinValue); var maxEndDate = actionTasks .Where(t => t.SDate.HasValue && t.Duration.HasValue) .Select(t => t.SDate!.Value.AddDays(t.Duration!.Value)) .DefaultIfEmpty(minDate) .Max(); actionEndDate = maxEndDate.ToDateTime(TimeOnly.MinValue); } // Add action as child of floor ganttData.Add(new GanttTaskViewModel { TaskId = taskOrder++, TaskName = action.EffectiveDisplayNameA, StartDate = actionStartDate, EndDate = actionEndDate, Duration = (actionEndDate - actionStartDate).Days, Progress = actionTasks.Average(t => t.Completion ?? 0), ParentId = floorTaskId, IsParent = true, Level = 1 }); int actionTaskId = taskOrder - 1; // Add individual tasks foreach (var task in actionTasks) { var taskStartDate = task.SDate.HasValue ? task.SDate.Value.ToDateTime(TimeOnly.MinValue) : DateTime.Today; var taskDuration = task.Duration ?? 1; var taskEndDate = taskStartDate.AddDays(taskDuration); ganttData.Add(new GanttTaskViewModel { TaskId = taskOrder++, TaskName = task.EffectiveDisplayName, StartDate = taskStartDate, EndDate = taskEndDate, Duration = taskDuration, Progress = task.Completion ?? 0, ParentId = actionTaskId, IsParent = false, Level = 2, Notes = task.Reference_Id }); } } } var viewModel = new GanttChartViewModel { Project = project, GanttTasks = ganttData, ProjectStartDate = ganttData.Any() ? ganttData.Min(t => t.StartDate) : DateTime.Today, ProjectEndDate = ganttData.Any() ? ganttData.Max(t => t.EndDate) : DateTime.Today.AddMonths(6) }; return View("Contractor_Dashboard/GanttChartView", viewModel); } // Helper method to calculate floor progress private double CalculateFloorProgress(int floorId, List actions, List tasks) { var floorActions = actions.Where(a => a.FloorId == floorId).ToList(); if (!floorActions.Any()) return 0; var floorTasks = new List(); foreach (var action in floorActions) { floorTasks.AddRange(tasks.Where(t => t.ActionId == action.ActionId)); } if (!floorTasks.Any()) return 0; return floorTasks.Average(t => t.Completion ?? 0); } // Gantt Chart Selection [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult GanttChartSelection() { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var projects = _unitOfWork.ODCProject.GetAll() .Where(p => p.UserId == userId) .Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }) .ToList(); ViewBag.Projects = projects; return View("Contractor_Dashboard/GanttChartSelection"); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult GanttChartSelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("GanttChartSelection"); } return RedirectToAction("GanttChartView", new { id = projectId }); } // ==================================================================================== // CONTRACTOR DASHBOARD - BUDGET MONITOR // ==================================================================================== [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult BudgetMonitor(int id) { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var project = _unitOfWork.ODCProject.Get( u => u.Id == id && u.UserId == userId, includeProperties: "User" ); if (project == null) { TempData["error"] = "Project not found or you don't have permission to view it"; return RedirectToAction("ViewAllProjects"); } // Get all floors, actions, and tasks for the project List actions = new List(); List floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == project.Id).ToList(); foreach (var floor in floors) { List actions_part = _unitOfWork.Action .Where(u => u.FloorId == floor.FloorId).ToList(); actions.AddRange(actions_part); } var actionGroups = new List(); foreach (var action in actions) { var tasks = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId).ToList(); var floor = floors.FirstOrDefault(f => f.FloorId == action.FloorId); actionGroups.Add(new BudgetActionGroupViewModel { Action = action, Floor = floor, Tasks = tasks }); } var allTasks = actionGroups.SelectMany(g => g.Tasks).ToList(); // Calculate project summary var projectSummary = new BudgetSummaryViewModel { // Calculated Budget (Original) TotalLaborCost = allTasks.Sum(t => t.Labor_Cost ?? 0), TotalMaterialCost = allTasks.Sum(t => t.Material_Cost ?? 0), TotalCalculatedBudget = allTasks.Sum(t => t.Mat_Lab ?? 0), // Modified Budget (Projected) TotalProjectedLaborCost = allTasks.Sum(t => t.M_Labor_Cost ?? 0), TotalProjectedMaterialCost = allTasks.Sum(t => t.M_Mat_Cost ?? 0), TotalProjectedBudget = allTasks.Sum(t => t.M_Budget ?? 0), // Actual Budget TotalActualLaborCost = allTasks.Sum(t => t.A_Labor_Cost ?? 0), TotalActualMaterialCost = allTasks.Sum(t => t.A_Mat_Cost ?? 0), TotalBudgetLeft = allTasks.Sum(t => t.A_Budget_Left ?? 0), TotalProfitLoss = allTasks.Sum(t => t.T_Profit_Loss ?? 0), // Variances CalculatedVsProjected = (allTasks.Sum(t => t.M_Budget ?? 0) - allTasks.Sum(t => t.Mat_Lab ?? 0)), ProjectedVsActual = (allTasks.Sum(t => t.M_Budget ?? 0) - (allTasks.Sum(t => t.A_Labor_Cost ?? 0) + allTasks.Sum(t => t.A_Mat_Cost ?? 0))), // Completion tracking TotalTasks = allTasks.Count, CompletedTasks = allTasks.Count(t => (t.Completion ?? 0) >= 100), InProgressTasks = allTasks.Count(t => (t.Completion ?? 0) > 0 && (t.Completion ?? 0) < 100), NotStartedTasks = allTasks.Count(t => (t.Completion ?? 0) == 0), AverageCompletion = allTasks.Any() ? allTasks.Average(t => t.Completion ?? 0) : 0 }; var viewModel = new BudgetMonitorViewModel { Project = project, ProjectSummary = projectSummary, ActionGroups = actionGroups.OrderBy(g => g.Floor?.Name).ThenBy(g => g.Action.Name).ToList() }; return View("Contractor_Dashboard/BudgetMonitor", viewModel); } // Budget Monitor Selection [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult BudgetMonitorSelection() { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var projects = _unitOfWork.ODCProject.GetAll() .Where(p => p.UserId == userId) .Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }) .ToList(); ViewBag.Projects = projects; return View("Contractor_Dashboard/BudgetMonitorSelection"); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult BudgetMonitorSelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("BudgetMonitorSelection"); } return RedirectToAction("BudgetMonitor", new { id = projectId }); } // ==================================================================================== // CONTRACTOR DASHBOARD - CONSTRUCTION REPORTS (DAILY CONSTRUCTION SHEETS) // ==================================================================================== [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult ConstructionReports(int id) { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var project = _unitOfWork.ODCProject.Get( u => u.Id == id && u.UserId == userId, includeProperties: "User" ); if (project == null) { TempData["error"] = "Project not found or you don't have permission to view it"; return RedirectToAction("ViewAllProjects"); } // Get all subtasks (construction reports) for this project var subtasks = _unitOfWork.Subtask.Where(s => s.ProjectId == id) .OrderByDescending(s => s.StartTimeStamp) .ToList(); // Get related tasks, actions, and floors for context var taskIds = subtasks.Select(s => s.TaskId).Distinct().ToList(); var tasks = new Dictionary(); var actions = new Dictionary(); var floors = new Dictionary(); foreach (var taskId in taskIds) { var task = _unitOfWork.Task.Get(t => t.TaskId == taskId); if (task != null) { tasks[taskId] = task; var action = _unitOfWork.Action.Get(a => a.ActionId == task.ActionId); if (action != null && !actions.ContainsKey(action.ActionId)) { actions[action.ActionId] = action; var floor = _unitOfWork.Floor.Get(f => f.FloorId == action.FloorId); if (floor != null && !floors.ContainsKey(floor.FloorId)) { floors[floor.FloorId] = floor; } } } } // Create view models with full context var reportViewModels = new List(); foreach (var subtask in subtasks) { var task = tasks.ContainsKey(subtask.TaskId) ? tasks[subtask.TaskId] : null; var action = task != null && actions.ContainsKey(task.ActionId) ? actions[task.ActionId] : null; var floor = action != null && floors.ContainsKey(action.FloorId) ? floors[action.FloorId] : null; // Get materials used in this subtask var materials = _unitOfWork.Material.Where(m => m.SubtaskId == subtask.SubtaskId).ToList(); var duration = (subtask.EndTimeStamp - subtask.StartTimeStamp).TotalHours; reportViewModels.Add(new ConstructionReportViewModel { Subtask = subtask, TaskName = task?.EffectiveDisplayName ?? "Unknown Task", ActionName = action?.EffectiveDisplayNameA ?? "Unknown Action", FloorName = floor?.Name ?? "Unknown Floor", TaskId = subtask.TaskId, ActionId = action?.ActionId ?? 0, FloorId = floor?.FloorId ?? 0, Duration = duration, HasAttachment = subtask.FileData != null && subtask.FileData.Length > 0, MaterialsUsed = materials, MaterialCount = materials.Count, TotalMaterialCost = materials.Sum(m => (m.Actual_Qty ?? 0) * (m.Unit_Cost ?? 0)) }); } // Group reports by date var groupedReports = reportViewModels .GroupBy(r => r.Subtask.StartTimeStamp.Date) .OrderByDescending(g => g.Key) .ToDictionary(g => g.Key, g => g.ToList()); var viewModel = new ConstructionReportsViewModel { Project = project, Reports = reportViewModels, GroupedReports = groupedReports, TotalReports = reportViewModels.Count, ReportsThisWeek = reportViewModels.Count(r => r.Subtask.StartTimeStamp >= DateTime.Now.AddDays(-7)), ReportsThisMonth = reportViewModels.Count(r => r.Subtask.StartTimeStamp >= DateTime.Now.AddMonths(-1)), TotalWorkingHours = reportViewModels.Sum(r => r.Duration) }; return View("Contractor_Dashboard/ConstructionReports", viewModel); } // View Single Report Details [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult ViewConstructionReport(int id) { var subtask = _unitOfWork.Subtask.Get(s => s.SubtaskId == id); if (subtask == null) { TempData["error"] = "Construction report not found"; return RedirectToAction("Contractor_Dashboard"); } // Verify ownership through project var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var project = _unitOfWork.ODCProject.Get(p => p.Id == subtask.ProjectId); if (project == null || project.UserId != userId) { TempData["error"] = "You don't have permission to view this report"; return RedirectToAction("Contractor_Dashboard"); } var task = _unitOfWork.Task.Get(t => t.TaskId == subtask.TaskId); var action = task != null ? _unitOfWork.Action.Get(a => a.ActionId == task.ActionId) : null; var floor = action != null ? _unitOfWork.Floor.Get(f => f.FloorId == action.FloorId) : null; var materials = _unitOfWork.Material.Where(m => m.SubtaskId == subtask.SubtaskId).ToList(); var viewModel = new ConstructionReportViewModel { Subtask = subtask, TaskName = task?.EffectiveDisplayName ?? "Unknown Task", ActionName = action?.EffectiveDisplayNameA ?? "Unknown Action", FloorName = floor?.Name ?? "Unknown Floor", TaskId = subtask.TaskId, ActionId = action?.ActionId ?? 0, FloorId = floor?.FloorId ?? 0, Duration = (subtask.EndTimeStamp - subtask.StartTimeStamp).TotalHours, HasAttachment = subtask.FileData != null && subtask.FileData.Length > 0, MaterialsUsed = materials, MaterialCount = materials.Count, TotalMaterialCost = materials.Sum(m => (m.Actual_Qty ?? 0) * (m.Unit_Cost ?? 0)), Project = project }; return View("Contractor_Dashboard/ViewConstructionReport", viewModel); } // Download Attachment [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult DownloadReportAttachment(int id) { var subtask = _unitOfWork.Subtask.Get(s => s.SubtaskId == id); if (subtask == null || subtask.FileData == null) { TempData["error"] = "Attachment not found"; return RedirectToAction("ConstructionReports", new { id = subtask?.ProjectId }); } // Verify ownership var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var project = _unitOfWork.ODCProject.Get(p => p.Id == subtask.ProjectId); if (project == null || project.UserId != userId) { TempData["error"] = "You don't have permission to download this attachment"; return RedirectToAction("Contractor_Dashboard"); } return File(subtask.FileData, subtask.FileContentType ?? "application/octet-stream", subtask.FileName ?? "attachment"); } // Delete Construction Report [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult DeleteConstructionReport(int id) { var subtask = _unitOfWork.Subtask.Get(s => s.SubtaskId == id); if (subtask == null) { TempData["error"] = "Construction report not found"; return RedirectToAction("Contractor_Dashboard"); } // Verify ownership var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var project = _unitOfWork.ODCProject.Get(p => p.Id == subtask.ProjectId); if (project == null || project.UserId != userId) { TempData["error"] = "You don't have permission to delete this report"; return RedirectToAction("Contractor_Dashboard"); } int projectId = subtask.ProjectId; _unitOfWork.Subtask.Remove(subtask); _unitOfWork.save(); TempData["success"] = "Construction report deleted successfully"; return RedirectToAction("ConstructionReports", new { id = projectId }); } // Construction Reports Selection [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult ConstructionReportsSelection() { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); var projects = _unitOfWork.ODCProject.GetAll() .Where(p => p.UserId == userId) .Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }) .ToList(); ViewBag.Projects = projects; return View("Contractor_Dashboard/ConstructionReportsSelection"); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult ConstructionReportsSelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("ConstructionReportsSelection"); } return RedirectToAction("ConstructionReports", new { id = projectId }); } // ==================================================================================== // QS DASHBOARD ACTIONS // ==================================================================================== // Parameter Entry - Project Selection [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult ParameterEntrySelection() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult ParameterEntrySelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("ParameterEntrySelection"); } // Redirect to Calculation controller's EnterCompleteData action return RedirectToAction("EnterCompleteData", "Calculation", new { area = "Staff", id = projectId }); } // Material List Entry - Project Selection [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult MaterialListEntrySelection() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult MaterialListEntrySelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("MaterialListEntrySelection"); } // Redirect to Calculation controller's EnterMaterial action return RedirectToAction("EnterMaterial", "Calculation", new { area = "Staff", id = projectId }); } // Completion Entry - Project Selection [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult CompletionEntrySelection() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult CompletionEntrySelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("CompletionEntrySelection"); } // Redirect to TaskManagement which handles completion entry return RedirectToAction("TaskManagement", "ODCProject", new { area = "Staff", id = projectId }); } // ==================================================================================== // MATERIAL MANAGEMENT - VIEW MATERIAL LISTS // ==================================================================================== [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ViewMaterialLists() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] public IActionResult ViewMaterialLists(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("ViewMaterialLists"); } return RedirectToAction("MaterialListsByProject", new { id = projectId }); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult MaterialListsByProject(int id) { try { Refresh_Calculations(); var project = _unitOfWork.ODCProject.Get(u => u.Id == id); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("ViewMaterialLists"); } // Get all floors for the project var floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == id).ToList(); var allMaterials = new List(); var materialsByFloor = new Dictionary>(); foreach (var floor in floors) { var floorMaterials = new List(); // Get all actions for this floor var actions = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId).ToList(); foreach (var action in actions) { // Get all tasks for this action var tasks = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId).ToList(); foreach (var task in tasks) { // Get all materials for this task var materials = _unitOfWork.Material.Where(u => u.TaskId == task.TaskId).ToList(); if (materials.Any()) { floorMaterials.Add(new MaterialsByAction { ActionName = action.EffectiveDisplayNameA, TaskName = task.Name, TaskId = task.TaskId, Materials = materials }); allMaterials.AddRange(materials); } } } if (floorMaterials.Any()) { materialsByFloor.Add(floor.Name, floorMaterials); } } // Calculate summary statistics var totalPlannedCost = allMaterials.Sum(m => m.Qty * (m.Unit_Cost ?? 0)); var totalActualCost = allMaterials.Sum(m => (m.Actual_Qty ?? 0) * (m.ActualCost ?? 0)); var materialsWithActual = allMaterials.Count(m => m.Actual_Qty.HasValue && m.Actual_Qty > 0); ViewBag.Project = project; ViewBag.ProjectId = id; ViewBag.MaterialsByFloor = materialsByFloor; ViewBag.TotalMaterials = allMaterials.Count; ViewBag.TotalPlannedCost = totalPlannedCost; ViewBag.TotalActualCost = totalActualCost; ViewBag.MaterialsWithActual = materialsWithActual; ViewBag.TrackingPercentage = allMaterials.Count > 0 ? (materialsWithActual * 100.0 / allMaterials.Count) : 0; return View(); } catch (Exception ex) { TempData["error"] = "Error loading material lists: " + ex.Message; return RedirectToAction("ViewMaterialLists"); } } // Helper class for organizing materials public class MaterialsByAction { public string ActionName { get; set; } public string TaskName { get; set; } public int TaskId { get; set; } public List Materials { get; set; } } // AJAX endpoint to get materials for a specific task (for quick view) [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult GetTaskMaterialsJson(int taskId) { var materials = _unitOfWork.Material .Where(m => m.TaskId == taskId) .Select(m => new { materialId = m.MaterialId, name = m.Name, plannedQty = m.Qty, actualQty = m.Actual_Qty ?? 0, unit = m.Unit, unitCost = m.Unit_Cost ?? 0, plannedCost = m.Qty * (m.Unit_Cost ?? 0), actualCost = (m.Actual_Qty ?? 0) * (m.Unit_Cost ?? 0), variance = ((m.Actual_Qty ?? 0) * (m.ActualCost ?? 0)) - (m.Qty * (m.Unit_Cost ?? 0)) }).ToList(); return Json(materials); } // Export material list to Excel [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ExportMaterialListExcel(int projectId) { try { var project = _unitOfWork.ODCProject.Get(u => u.Id == projectId); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("ViewMaterialLists"); } // Use the existing material report generation return (IActionResult)GenerateProjectMaterialReport(projectId); } catch (Exception ex) { TempData["error"] = "Error exporting material list: " + ex.Message; return RedirectToAction("MaterialListsByProject", new { id = projectId }); } } /////////////////////////////////////////////////////////////////////////////////////// // ==================================================================================== // MATERIAL USAGE ANALYSIS // ==================================================================================== [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult MaterialUsageAnalysis() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] public IActionResult MaterialUsageAnalysis(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("MaterialUsageAnalysis"); } return RedirectToAction("MaterialUsageAnalysisReport", new { id = projectId }); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult MaterialUsageAnalysisReport(int id) { try { Refresh_Calculations(); var project = _unitOfWork.ODCProject.Get(u => u.Id == id); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("MaterialUsageAnalysis"); } // Get all materials for the project var allMaterials = _unitOfWork.Material .Where(m => m.ODCProjectId == id) .ToList(); // Calculate statistics var materialsWithActual = allMaterials .Where(m => m.Actual_Qty.HasValue && m.Actual_Qty > 0) .ToList(); var totalPlannedCost = allMaterials.Sum(m => m.Qty * (m.Unit_Cost ?? 0)); var totalActualCost = allMaterials.Sum(m => (m.Actual_Qty ?? 0) * (m.ActualCost ?? 0)); var totalVariance = totalActualCost - totalPlannedCost; // Group materials by consumption status var overBudgetMaterials = materialsWithActual .Where(m => (m.Actual_Qty ?? 0) > m.Qty) .OrderByDescending(m => ((m.Actual_Qty ?? 0) - m.Qty) * (m.ActualCost ?? 0)) .Take(10) .ToList(); var underBudgetMaterials = materialsWithActual .Where(m => (m.Actual_Qty ?? 0) < m.Qty) .OrderByDescending(m => (m.Qty - (m.Actual_Qty ?? 0)) * (m.ActualCost ?? 0)) .Take(10) .ToList(); var topCostMaterials = allMaterials .OrderByDescending(m => m.Qty * (m.Unit_Cost ?? 0)) .Take(10) .ToList(); // Calculate efficiency metrics var efficiencyRate = totalPlannedCost > 0 ? ((totalPlannedCost - Math.Abs(totalVariance)) / totalPlannedCost) * 100 : 0; var trackingCompleteness = allMaterials.Count > 0 ? (materialsWithActual.Count * 100.0 / allMaterials.Count) : 0; // Group materials by category (based on name patterns) var materialCategories = GroupMaterialsByCategory(allMaterials); ViewBag.Project = project; ViewBag.ProjectId = id; ViewBag.TotalMaterials = allMaterials.Count; ViewBag.MaterialsTracked = materialsWithActual.Count; ViewBag.TotalPlannedCost = totalPlannedCost; ViewBag.TotalActualCost = totalActualCost; ViewBag.TotalVariance = totalVariance; ViewBag.VariancePercentage = totalPlannedCost > 0 ? (totalVariance / totalPlannedCost) * 100 : 0; ViewBag.EfficiencyRate = efficiencyRate; ViewBag.TrackingCompleteness = trackingCompleteness; ViewBag.OverBudgetMaterials = overBudgetMaterials; ViewBag.UnderBudgetMaterials = underBudgetMaterials; ViewBag.TopCostMaterials = topCostMaterials; ViewBag.MaterialCategories = materialCategories; return View(); } catch (Exception ex) { TempData["error"] = "Error generating analysis: " + ex.Message; return RedirectToAction("MaterialUsageAnalysis"); } } // Helper method to group materials by category private Dictionary> GroupMaterialsByCategory(List materials) { var categories = new Dictionary>(); foreach (var material in materials) { string category = DetermineMaterialCategory(material.Name); if (!categories.ContainsKey(category)) { categories[category] = new List(); } categories[category].Add(material); } return categories; } // Helper method to determine material category private string DetermineMaterialCategory(string materialName) { var name = materialName.ToLower(); if (name.Contains("cement") || name.Contains("concrete") || name.Contains("mortar")) return "Cement & Concrete"; if (name.Contains("steel") || name.Contains("rod") || name.Contains("bar") || name.Contains("reinforcement")) return "Steel & Reinforcement"; if (name.Contains("brick") || name.Contains("block") || name.Contains("tile")) return "Masonry"; if (name.Contains("paint") || name.Contains("putty") || name.Contains("primer")) return "Finishing Materials"; if (name.Contains("sand") || name.Contains("gravel") || name.Contains("aggregate") || name.Contains("stone")) return "Aggregates"; if (name.Contains("wood") || name.Contains("timber") || name.Contains("plywood")) return "Timber & Wood"; if (name.Contains("pipe") || name.Contains("plumbing") || name.Contains("fitting")) return "Plumbing"; if (name.Contains("wire") || name.Contains("cable") || name.Contains("electrical")) return "Electrical"; return "Other Materials"; } /////////////////////////////////////////////////////////////////////////////////////// // ==================================================================================== // VARIANCE REPORTS // ==================================================================================== [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult VarianceReports() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] public IActionResult VarianceReports(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("VarianceReports"); } return RedirectToAction("VarianceReportDetails", new { id = projectId }); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult VarianceReportDetails(int id) { try { Refresh_Calculations(); var project = _unitOfWork.ODCProject.Get(u => u.Id == id); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("VarianceReports"); } // Get all materials with variance data var allMaterials = _unitOfWork.Material .Where(m => m.ODCProjectId == id) .ToList(); // Get materials with actual quantities tracked var trackedMaterials = allMaterials .Where(m => m.Actual_Qty.HasValue && m.Actual_Qty > 0) .ToList(); // Calculate material variances var materialVariances = new List(); foreach (var material in trackedMaterials) { var task = _unitOfWork.Task.Get(t => t.TaskId == material.TaskId); var action = task != null ? _unitOfWork.Action.Get(a => a.ActionId == task.ActionId) : null; var floor = action != null ? _unitOfWork.Floor.Get(f => f.FloorId == action.FloorId) : null; var qtyVariance = (material.Actual_Qty ?? 0) - material.Qty; var qtyVariancePercent = material.Qty > 0 ? (qtyVariance / material.Qty) * 100 : 0; var costVariance = material.CostVariance ?? 0; var costVariancePercent = (material.Qty * (material.Unit_Cost ?? 0)) > 0 ? (costVariance / (material.Qty * (material.Unit_Cost ?? 0))) * 100 : 0; materialVariances.Add(new MaterialVarianceData { MaterialName = material.Name, FloorName = floor?.Name ?? "N/A", ActionName = action?.EffectiveDisplayNameA ?? "N/A", TaskName = task?.Name ?? "N/A", PlannedQty = material.Qty, ActualQty = material.Actual_Qty ?? 0, QtyVariance = qtyVariance, QtyVariancePercent = qtyVariancePercent, Unit = material.Unit ?? "N/A", UnitCost = material.Unit_Cost ?? 0, PlannedCost = material.Qty * (material.Unit_Cost ?? 0), ActualCost = material.ActualCost ?? 0, CostVariance = costVariance, CostVariancePercent = costVariancePercent, RecordedDate = material.ActualQtyRecordedDate, RecordedBy = material.RecordedBy }); } // Get all tasks for labor variance var allTasks = new List(); var floors = _unitOfWork.Floor.Where(f => f.ODCProjectId == id).ToList(); foreach (var floor in floors) { var actions = _unitOfWork.Action.Where(a => a.FloorId == floor.FloorId).ToList(); foreach (var action in actions) { var tasks = _unitOfWork.Task.Where(t => t.ActionId == action.ActionId).ToList(); allTasks.AddRange(tasks); } } // Calculate labor variances var laborVariances = new List(); foreach (var task in allTasks.Where(t => t.Completion.HasValue && t.Completion > 0)) { var action = _unitOfWork.Action.Get(a => a.ActionId == task.ActionId); var floor = action != null ? _unitOfWork.Floor.Get(f => f.FloorId == action.FloorId) : null; var measuredWorkVariance = (task.Measured_Work ?? 0) - task.Measured_Work ?? 0; // Simplified - you may have planned vs actual var laborCostVariance = (task.A_Labor_Cost ?? 0) - (task.M_Labor_Cost ?? 0); var laborCostVariancePercent = (task.M_Labor_Cost ?? 0) > 0 ? (laborCostVariance / (task.M_Labor_Cost ?? 0)) * 100 : 0; laborVariances.Add(new LaborVarianceData { TaskName = task.Name, FloorName = floor?.Name ?? "N/A", ActionName = action?.EffectiveDisplayNameA ?? "N/A", PlannedLaborCost = task.M_Labor_Cost ?? 0, ActualLaborCost = task.A_Labor_Cost ?? 0, LaborCostVariance = laborCostVariance, LaborCostVariancePercent = laborCostVariancePercent, Completion = task.Completion ?? 0, LaborRate = task.Labor_Rate, MeasuredWork = task.Measured_Work ?? 0 }); } // Calculate summary statistics var totalMaterialPlannedCost = materialVariances.Sum(m => m.PlannedCost); var totalMaterialActualCost = materialVariances.Sum(m => m.ActualCost); var totalMaterialVariance = totalMaterialActualCost - totalMaterialPlannedCost; var totalMaterialVariancePercent = totalMaterialPlannedCost > 0 ? (totalMaterialVariance / totalMaterialPlannedCost) * 100 : 0; var totalLaborPlannedCost = laborVariances.Sum(l => l.PlannedLaborCost); var totalLaborActualCost = laborVariances.Sum(l => l.ActualLaborCost); var totalLaborVariance = totalLaborActualCost - totalLaborPlannedCost; var totalLaborVariancePercent = totalLaborPlannedCost > 0 ? (totalLaborVariance / totalLaborPlannedCost) * 100 : 0; var overallPlannedCost = totalMaterialPlannedCost + totalLaborPlannedCost; var overallActualCost = totalMaterialActualCost + totalLaborActualCost; var overallVariance = overallActualCost - overallPlannedCost; var overallVariancePercent = overallPlannedCost > 0 ? (overallVariance / overallPlannedCost) * 100 : 0; // Significant variances (>10%) var significantMaterialVariances = materialVariances .Where(m => Math.Abs(m.CostVariancePercent) > 10) .OrderByDescending(m => Math.Abs(m.CostVariance)) .ToList(); var significantLaborVariances = laborVariances .Where(l => Math.Abs(l.LaborCostVariancePercent) > 10) .OrderByDescending(l => Math.Abs(l.LaborCostVariance)) .ToList(); ViewBag.Project = project; ViewBag.ProjectId = id; ViewBag.MaterialVariances = materialVariances.OrderByDescending(m => Math.Abs(m.CostVariance)).ToList(); ViewBag.LaborVariances = laborVariances.OrderByDescending(l => Math.Abs(l.LaborCostVariance)).ToList(); ViewBag.SignificantMaterialVariances = significantMaterialVariances; ViewBag.SignificantLaborVariances = significantLaborVariances; ViewBag.TotalMaterialPlannedCost = totalMaterialPlannedCost; ViewBag.TotalMaterialActualCost = totalMaterialActualCost; ViewBag.TotalMaterialVariance = totalMaterialVariance; ViewBag.TotalMaterialVariancePercent = totalMaterialVariancePercent; ViewBag.TotalLaborPlannedCost = totalLaborPlannedCost; ViewBag.TotalLaborActualCost = totalLaborActualCost; ViewBag.TotalLaborVariance = totalLaborVariance; ViewBag.TotalLaborVariancePercent = totalLaborVariancePercent; ViewBag.OverallPlannedCost = overallPlannedCost; ViewBag.OverallActualCost = overallActualCost; ViewBag.OverallVariance = overallVariance; ViewBag.OverallVariancePercent = overallVariancePercent; ViewBag.MaterialsTracked = trackedMaterials.Count; ViewBag.TotalMaterials = allMaterials.Count; ViewBag.TasksTracked = laborVariances.Count; ViewBag.TotalTasks = allTasks.Count; return View(); } catch (Exception ex) { TempData["error"] = "Error generating variance report: " + ex.Message; return RedirectToAction("VarianceReports"); } } // Helper classes for variance data public class MaterialVarianceData { public string MaterialName { get; set; } public string FloorName { get; set; } public string ActionName { get; set; } public string TaskName { get; set; } public double PlannedQty { get; set; } public double ActualQty { get; set; } public double QtyVariance { get; set; } public double QtyVariancePercent { get; set; } public string Unit { get; set; } public double UnitCost { get; set; } public double PlannedCost { get; set; } public double ActualCost { get; set; } public double CostVariance { get; set; } public double CostVariancePercent { get; set; } public DateTime? RecordedDate { get; set; } public string RecordedBy { get; set; } } public class LaborVarianceData { public string TaskName { get; set; } public string FloorName { get; set; } public string ActionName { get; set; } public double PlannedLaborCost { get; set; } public double ActualLaborCost { get; set; } public double LaborCostVariance { get; set; } public double LaborCostVariancePercent { get; set; } public double Completion { get; set; } public double LaborRate { get; set; } public double MeasuredWork { get; set; } } /////////////////////////////////////////////////////////////////////////////////////// // ==================================================================================== // EXPORT MATERIAL DATA // ==================================================================================== [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ExportMaterialData() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] public IActionResult ExportMaterialData(int projectId, string exportFormat = "excel") { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("ExportMaterialData"); } // Use existing material report generation if (exportFormat == "excel") { return (IActionResult)GenerateProjectMaterialReport(projectId); } else if (exportFormat == "csv") { return ExportMaterialDataCSV(projectId); } TempData["error"] = "Invalid export format"; return RedirectToAction("ExportMaterialData"); } // CSV Export option (lightweight alternative) [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ExportMaterialDataCSV(int projectId) { try { var project = _unitOfWork.ODCProject.Get(u => u.Id == projectId); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("ExportMaterialData"); } var floors = _unitOfWork.Floor.Where(f => f.ODCProjectId == projectId).ToList(); var allMaterials = new List(); // Collect all materials foreach (var floor in floors) { var actions = _unitOfWork.Action.Where(a => a.FloorId == floor.FloorId).ToList(); foreach (var action in actions) { var tasks = _unitOfWork.Task.Where(t => t.ActionId == action.ActionId).ToList(); foreach (var task in tasks) { var materials = _unitOfWork.Material.Where(m => m.TaskId == task.TaskId).ToList(); allMaterials.AddRange(materials); } } } // Build CSV content var csv = new System.Text.StringBuilder(); // Header csv.AppendLine("Floor,Action,Task,Material Name,Unit,Planned Qty,Actual Qty,Qty Variance,Unit Cost,Planned Cost,Actual Cost,Cost Variance,Recorded Date,Recorded By"); // Data rows foreach (var material in allMaterials) { var task = _unitOfWork.Task.Get(t => t.TaskId == material.TaskId); var action = task != null ? _unitOfWork.Action.Get(a => a.ActionId == task.ActionId) : null; var floor = action != null ? _unitOfWork.Floor.Get(f => f.FloorId == action.FloorId) : null; var plannedCost = material.Qty * (material.Unit_Cost ?? 0); var actualCost = (material.Actual_Qty ?? 0) * (material.Unit_Cost ?? 0); var qtyVariance = (material.Actual_Qty ?? 0) - material.Qty; var costVariance = actualCost - plannedCost; csv.AppendLine($"\"{floor?.Name ?? "N/A"}\"," + $"\"{action?.EffectiveDisplayNameA ?? "N/A"}\"," + $"\"{task?.Name ?? "N/A"}\"," + $"\"{material.Name}\"," + $"\"{material.Unit ?? "N/A"}\"," + $"{material.Qty}," + $"{material.Actual_Qty ?? 0}," + $"{qtyVariance}," + $"{material.Unit_Cost ?? 0}," + $"{plannedCost}," + $"{actualCost}," + $"{costVariance}," + $"\"{material.ActualQtyRecordedDate?.ToString("yyyy-MM-dd HH:mm") ?? ""}\"," + $"\"{material.RecordedBy ?? ""}\""); } var fileName = $"MaterialData_{project.Name.Replace(" ", "_")}_{DateTime.Now:yyyyMMdd_HHmm}.csv"; var bytes = System.Text.Encoding.UTF8.GetBytes(csv.ToString()); return File(bytes, "text/csv", fileName); } catch (Exception ex) { TempData["error"] = "Error exporting material data: " + ex.Message; return RedirectToAction("ExportMaterialData"); } } //////////////////////////////////////////////////////////////////// // ==================================================================================== // VIEW PROGRESS REPORTS - CONSTRUCTION SHEETS // ==================================================================================== [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ViewProgressReportsSelection() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] public IActionResult ViewProgressReportsSelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("ViewProgressReportsSelection"); } return RedirectToAction("ProjectProgressReports", new { id = projectId }); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ProjectProgressReports(int id) { try { Refresh_Calculations(); var project = _unitOfWork.ODCProject.Get(u => u.Id == id); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("ViewProgressReportsSelection"); } // Get all floors for the project var floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == id).ToList(); // Get all actions for these floors var actions = new List(); foreach (var floor in floors) { var floorActions = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId).ToList(); actions.AddRange(floorActions); } // Get all tasks for these actions var tasks = new List(); foreach (var action in actions) { var actionTasks = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId).ToList(); tasks.AddRange(actionTasks); } // Get all construction sheets for these tasks var allConstructionSheets = new List(); foreach (var task in tasks) { var sheets = _unitOfWork.Subtask.Where(s => s.TaskId == task.TaskId).ToList(); allConstructionSheets.AddRange(sheets); } // Build the list of construction sheet details var constructionSheetDetails = new List(); foreach (var sheet in allConstructionSheets.OrderByDescending(s => s.StartTimeStamp)) { var task = tasks.FirstOrDefault(t => t.TaskId == sheet.TaskId); if (task != null) { var action = actions.FirstOrDefault(a => a.ActionId == task.ActionId); var floor = action != null ? floors.FirstOrDefault(f => f.FloorId == action.FloorId) : null; // Calculate duration - StartTimeStamp and EndTimeStamp are NOT nullable double duration = (sheet.EndTimeStamp - sheet.StartTimeStamp).TotalHours; constructionSheetDetails.Add(new ConstructionSheetDetail { Subtask = sheet, TaskName = task.Name ?? "N/A", TaskCompletion = task.Completion, ActionName = action?.Name ?? "N/A", FloorName = floor?.Name ?? "N/A", Duration = duration }); } } // Calculate project statistics var totalSheets = constructionSheetDetails.Count; var totalTasks = tasks.Count; var completedTasks = tasks.Count(t => t.Completion == 100); var inProgressTasks = tasks.Count(t => t.Completion > 0 && t.Completion < 100); var notStartedTasks = tasks.Count(t => t.Completion == 0 || t.Completion == null); var averageCompletion = tasks.Any() ? tasks.Average(t => t.Completion ?? 0) : 0; ViewBag.Project = project; ViewBag.TotalSheets = totalSheets; ViewBag.TotalTasks = totalTasks; ViewBag.CompletedTasks = completedTasks; ViewBag.InProgressTasks = inProgressTasks; ViewBag.NotStartedTasks = notStartedTasks; ViewBag.AverageCompletion = averageCompletion; ViewBag.ProjectId = id; return View(constructionSheetDetails); } catch (Exception ex) { TempData["error"] = "Error loading progress reports: " + ex.Message; System.Diagnostics.Debug.WriteLine($"ProjectProgressReports Error: {ex.Message}"); return RedirectToAction("ViewProgressReportsSelection"); } } // Helper class for construction sheet details public class ConstructionSheetDetail { public Subtask Subtask { get; set; } public string TaskName { get; set; } public double? TaskCompletion { get; set; } public string ActionName { get; set; } public string FloorName { get; set; } public double Duration { get; set; } } /////////////////////////////////////////////////////////////////////////////////////// // ==================================================================================== // TIMELINE ANALYSIS - GANTT CHART // ==================================================================================== [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult TimelineAnalysisSelection() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] public IActionResult TimelineAnalysisSelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("TimelineAnalysisSelection"); } return RedirectToAction("ProjectGanttChart", new { id = projectId }); } ///////////////////////////////////////////////////////////////////////////////////////# // ==================================================================================== // PROGRESS DOCUMENTATION // ==================================================================================== [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ProgressDocumentationSelection() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] public IActionResult ProgressDocumentationSelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("ProgressDocumentationSelection"); } return RedirectToAction("ProjectProgressDocumentation", new { id = projectId }); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ProjectProgressDocumentation(int id) { try { Refresh_Calculations(); var project = _unitOfWork.ODCProject.Get(u => u.Id == id); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("ProgressDocumentationSelection"); } // Get all floors for the project var floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == id).ToList(); // Get all actions for these floors var actions = new List(); foreach (var floor in floors) { var floorActions = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId).ToList(); actions.AddRange(floorActions); } // Get all tasks for these actions var tasks = new List(); foreach (var action in actions) { var actionTasks = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId).ToList(); tasks.AddRange(actionTasks); } // Get all construction sheets for documentation var allConstructionSheets = new List(); foreach (var task in tasks) { var sheets = _unitOfWork.Subtask.Where(s => s.TaskId == task.TaskId).ToList(); allConstructionSheets.AddRange(sheets); } // Build documentation structure var documentationData = new List(); foreach (var floor in floors.OrderBy(f => f.FloorId)) { var floorActions = actions.Where(a => a.FloorId == floor.FloorId).ToList(); foreach (var action in floorActions.OrderBy(a => a.ActionId)) { var actionTasks = tasks.Where(t => t.ActionId == action.ActionId).ToList(); foreach (var task in actionTasks.OrderBy(t => t.TaskId)) { var taskSheets = allConstructionSheets .Where(s => s.TaskId == task.TaskId) .OrderByDescending(s => s.StartTimeStamp) .ToList(); documentationData.Add(new ProgressDocumentationItem { FloorName = floor.Name, ActionName = action.Name, TaskName = task.Name, TaskCompletion = task.Completion ?? 0, TaskId = task.TaskId, ConstructionSheets = taskSheets, HasDocumentation = taskSheets.Any(), TotalSheets = taskSheets.Count, LatestUpdate = taskSheets.Any() ? taskSheets.First().StartTimeStamp : (DateTime?)null }); } } } // Calculate statistics var totalTasks = documentationData.Count; var documentedTasks = documentationData.Count(d => d.HasDocumentation); var totalSheets = allConstructionSheets.Count; var tasksWithFiles = allConstructionSheets.Count(s => !string.IsNullOrEmpty(s.FileName)); var averageCompletion = tasks.Any() ? tasks.Average(t => t.Completion ?? 0) : 0; ViewBag.Project = project; ViewBag.ProjectId = id; ViewBag.TotalTasks = totalTasks; ViewBag.DocumentedTasks = documentedTasks; ViewBag.TotalSheets = totalSheets; ViewBag.TasksWithFiles = tasksWithFiles; ViewBag.AverageCompletion = averageCompletion; ViewBag.DocumentationRate = totalTasks > 0 ? (documentedTasks * 100.0 / totalTasks) : 0; return View(documentationData); } catch (Exception ex) { TempData["error"] = "Error loading progress documentation: " + ex.Message; System.Diagnostics.Debug.WriteLine($"ProjectProgressDocumentation Error: {ex.Message}"); return RedirectToAction("ProgressDocumentationSelection"); } } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ConstructionSheetDetaill(int id) { try { var sheet = _unitOfWork.Subtask.Get(s => s.SubtaskId == id); if (sheet == null) { TempData["error"] = "Construction sheet not found"; return RedirectToAction("ViewProgressReportsSelection"); } var task = _unitOfWork.Task.Get(t => t.TaskId == sheet.TaskId); var action = task != null ? _unitOfWork.Action.Get(a => a.ActionId == task.ActionId) : null; var floor = action != null ? _unitOfWork.Floor.Get(f => f.FloorId == action.FloorId) : null; var project = floor != null ? _unitOfWork.ODCProject.Get(p => p.Id == floor.ODCProjectId) : null; // Get materials linked to this construction sheet var materials = _unitOfWork.Material .Where(m => m.TaskId == sheet.TaskId && m.SubtaskId == sheet.SubtaskId) .ToList(); // Calculate duration double duration = (sheet.EndTimeStamp - sheet.StartTimeStamp).TotalHours; ViewBag.Sheet = sheet; ViewBag.Task = task; ViewBag.Action = action; ViewBag.Floor = floor; ViewBag.Project = project; ViewBag.Materials = materials; ViewBag.ProjectId = project?.Id ?? 0; ViewBag.Duration = duration; return View(sheet); } catch (Exception ex) { TempData["error"] = "Error loading construction sheet detail: " + ex.Message; System.Diagnostics.Debug.WriteLine($"ConstructionSheetDetail Error: {ex.Message}"); return RedirectToAction("ViewProgressReportsSelection"); } } // Helper class for progress documentation public class ProgressDocumentationItem { public string FloorName { get; set; } public string ActionName { get; set; } public string TaskName { get; set; } public double TaskCompletion { get; set; } public int TaskId { get; set; } public List ConstructionSheets { get; set; } public bool HasDocumentation { get; set; } public int TotalSheets { get; set; } public DateTime? LatestUpdate { get; set; } } /////////////////////////////////////////////////////////////////////////////////////// [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult GenerateProjectSummaryReport(int id) { try { Refresh_Calculations(); var project = _unitOfWork.ODCProject.Get(u => u.Id == id); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("ProjectSummaryReportSelection"); } // Get all project data var floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == id).ToList(); var actions = new List(); var tasks = new List(); foreach (var floor in floors) { var floorActions = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId).ToList(); actions.AddRange(floorActions); } foreach (var action in actions) { var actionTasks = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId).ToList(); tasks.AddRange(actionTasks); } // Get all construction sheets var allConstructionSheets = new List(); foreach (var task in tasks) { var sheets = _unitOfWork.Subtask.Where(s => s.TaskId == task.TaskId).ToList(); allConstructionSheets.AddRange(sheets); } // Calculate summary statistics var totalTasks = tasks.Count; var completedTasks = tasks.Count(t => t.Completion == 100); var inProgressTasks = tasks.Count(t => t.Completion > 0 && t.Completion < 100); var notStartedTasks = tasks.Count(t => t.Completion == 0 || t.Completion == null); var averageCompletion = tasks.Any() ? tasks.Average(t => t.Completion ?? 0) : 0; // Cost calculations var totalMaterialCost = tasks.Sum(t => t.Material_Cost ?? 0); var totalLaborCost = tasks.Sum(t => t.Labor_Cost ?? 0); var totalProjectCost = totalMaterialCost + totalLaborCost; // Budget calculations var totalBudget = tasks.Sum(t => (t.M_Budget ?? 0)); var totalActualCost = tasks.Sum(t => (t.A_Labor_Cost ?? 0) + (t.A_Mat_Cost ?? 0)); var budgetVariance = totalBudget - totalActualCost; // Documentation statistics var totalSheets = allConstructionSheets.Count; var tasksWithDocumentation = tasks.Count(t => allConstructionSheets.Any(s => s.TaskId == t.TaskId)); var documentationRate = totalTasks > 0 ? (tasksWithDocumentation * 100.0 / totalTasks) : 0; // Timeline calculations - FIXED DateOnly? earliestStart = null; DateOnly? latestEnd = null; int totalDuration = 0; // Get tasks with valid dates in memory var tasksWithStartDates = tasks.Where(t => t.SDate.HasValue).ToList(); if (tasksWithStartDates.Any()) { earliestStart = tasksWithStartDates.Min(t => t.SDate.Value); // Calculate latest end date foreach (var task in tasksWithStartDates) { if (task.Duration.HasValue) { var taskEndDate = task.SDate.Value.AddDays(task.Duration.Value); if (!latestEnd.HasValue || taskEndDate > latestEnd.Value) { latestEnd = taskEndDate; } } } // Calculate total duration if (earliestStart.HasValue && latestEnd.HasValue) { totalDuration = latestEnd.Value.DayNumber - earliestStart.Value.DayNumber; } } // Floor-wise breakdown var floorSummaries = new List(); foreach (var floor in floors) { var floorActions = actions.Where(a => a.FloorId == floor.FloorId).ToList(); var floorTasks = new List(); foreach (var action in floorActions) { floorTasks.AddRange(tasks.Where(t => t.ActionId == action.ActionId)); } floorSummaries.Add(new FloorSummary { FloorName = floor.Name, TotalTasks = floorTasks.Count, CompletedTasks = floorTasks.Count(t => t.Completion == 100), AverageCompletion = floorTasks.Any() ? floorTasks.Average(t => t.Completion ?? 0) : 0, MaterialCost = floorTasks.Sum(t => t.Material_Cost ?? 0), LaborCost = floorTasks.Sum(t => t.Labor_Cost ?? 0), TotalCost = floorTasks.Sum(t => (t.Material_Cost ?? 0) + (t.Labor_Cost ?? 0)) }); } // Recent activities var recentActivities = allConstructionSheets .OrderByDescending(s => s.StartTimeStamp) .Take(10) .Select(s => new RecentActivity { Date = s.StartTimeStamp, Description = s.SubTaskD, TaskName = tasks.FirstOrDefault(t => t.TaskId == s.TaskId)?.Name ?? "Unknown Task" }) .ToList(); // Package data for view var summaryData = new ProjectSummaryData { Project = project, TotalFloors = floors.Count, TotalActions = actions.Count, TotalTasks = totalTasks, CompletedTasks = completedTasks, InProgressTasks = inProgressTasks, NotStartedTasks = notStartedTasks, AverageCompletion = averageCompletion, TotalMaterialCost = totalMaterialCost, TotalLaborCost = totalLaborCost, TotalProjectCost = totalProjectCost, TotalBudget = totalBudget, TotalActualCost = totalActualCost, BudgetVariance = budgetVariance, TotalConstructionSheets = totalSheets, TasksWithDocumentation = tasksWithDocumentation, DocumentationRate = documentationRate, EarliestStart = earliestStart, LatestEnd = latestEnd, TotalDuration = totalDuration, FloorSummaries = floorSummaries, RecentActivities = recentActivities }; return View(summaryData); } catch (Exception ex) { TempData["error"] = "Error generating project summary: " + ex.Message; System.Diagnostics.Debug.WriteLine($"GenerateProjectSummaryReport Error: {ex.Message}"); return RedirectToAction("ProjectSummaryReportSelection"); } } /////////////////////////////////////////////////////////////////////////////////////// // ==================================================================================== // QUICK REPORTS - PROJECT SUMMARY // ==================================================================================== [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ProjectSummaryReportSelection() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] public IActionResult ProjectSummaryReportSelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("ProjectSummaryReportSelection"); } return RedirectToAction("GenerateProjectSummaryReport", new { id = projectId }); } // ==================================================================================== // QUICK REPORTS - COST ANALYSIS // ==================================================================================== [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult CostAnalysisReportSelection() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] public IActionResult CostAnalysisReportSelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("CostAnalysisReportSelection"); } return RedirectToAction("GenerateCostAnalysisReport", new { id = projectId }); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult GenerateCostAnalysisReport(int id) { try { Refresh_Calculations(); var project = _unitOfWork.ODCProject.Get(u => u.Id == id); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("CostAnalysisReportSelection"); } // Get all project data var floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == id).ToList(); var actions = new List(); var tasks = new List(); var materials = new List(); foreach (var floor in floors) { var floorActions = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId).ToList(); actions.AddRange(floorActions); } foreach (var action in actions) { var actionTasks = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId).ToList(); tasks.AddRange(actionTasks); } foreach (var task in tasks) { var taskMaterials = _unitOfWork.Material.Where(m => m.TaskId == task.TaskId).ToList(); materials.AddRange(taskMaterials); } // Overall Cost Summary var totalMaterialCost = tasks.Sum(t => t.Material_Cost ?? 0); var totalLaborCost = tasks.Sum(t => t.Labor_Cost ?? 0); var totalProjectCost = totalMaterialCost + totalLaborCost; // Budget Analysis var totalPlannedBudget = tasks.Sum(t => t.M_Budget ?? 0); var totalActualCost = tasks.Sum(t => (t.A_Labor_Cost ?? 0) + (t.A_Mat_Cost ?? 0)); var budgetVariance = totalPlannedBudget - totalActualCost; var budgetVariancePercentage = totalPlannedBudget > 0 ? (budgetVariance / totalPlannedBudget) * 100 : 0; // Material Cost Breakdown var materialsByCategory = materials .GroupBy(m => m.Name) .Select(g => new MaterialCostItem { MaterialName = g.Key, PlannedQuantity = g.Sum(m => m.Qty), ActualQuantity = g.Sum(m => m.Actual_Qty ?? 0), UnitCost = g.FirstOrDefault()?.Unit_Cost ?? 0, PlannedCost = g.Sum(m => m.Qty * (m.Unit_Cost ?? 0)), ActualCost = g.Sum(m => (m.Actual_Qty ?? 0) * (m.Unit_Cost ?? 0)), Unit = g.FirstOrDefault()?.Unit ?? "N/A" }) .OrderByDescending(m => m.PlannedCost) .Take(20) .ToList(); // Floor-wise Cost Analysis var floorCostBreakdown = new List(); foreach (var floor in floors) { var floorActions = actions.Where(a => a.FloorId == floor.FloorId).ToList(); var floorTasks = new List(); foreach (var action in floorActions) { floorTasks.AddRange(tasks.Where(t => t.ActionId == action.ActionId)); } var floorMaterialCost = floorTasks.Sum(t => t.Material_Cost ?? 0); var floorLaborCost = floorTasks.Sum(t => t.Labor_Cost ?? 0); var floorTotalCost = floorMaterialCost + floorLaborCost; floorCostBreakdown.Add(new FloorCostAnalysis { FloorName = floor.Name, MaterialCost = floorMaterialCost, LaborCost = floorLaborCost, TotalCost = floorTotalCost, PercentageOfTotal = totalProjectCost > 0 ? (floorTotalCost / totalProjectCost) * 100 : 0 }); } // Task Cost Analysis - Top 10 Most Expensive Tasks var topExpensiveTasks = tasks .Select(t => new TaskCostItem { TaskName = t.Name, FloorName = floors.FirstOrDefault(f => actions.Any(a => a.ActionId == t.ActionId && a.FloorId == f.FloorId))?.Name ?? "N/A", ActionName = actions.FirstOrDefault(a => a.ActionId == t.ActionId)?.Name ?? "N/A", MaterialCost = t.Material_Cost ?? 0, LaborCost = t.Labor_Cost ?? 0, TotalCost = (t.Material_Cost ?? 0) + (t.Labor_Cost ?? 0), Completion = t.Completion ?? 0 }) .OrderByDescending(t => t.TotalCost) .Take(10) .ToList(); // Cost Distribution var materialPercentage = totalProjectCost > 0 ? (totalMaterialCost / totalProjectCost) * 100 : 0; var laborPercentage = totalProjectCost > 0 ? (totalLaborCost / totalProjectCost) * 100 : 0; // Package data for view var costAnalysisData = new CostAnalysisData { Project = project, TotalMaterialCost = totalMaterialCost, TotalLaborCost = totalLaborCost, TotalProjectCost = totalProjectCost, TotalPlannedBudget = totalPlannedBudget, TotalActualCost = totalActualCost, BudgetVariance = budgetVariance, BudgetVariancePercentage = budgetVariancePercentage, MaterialPercentage = materialPercentage, LaborPercentage = laborPercentage, MaterialCostBreakdown = materialsByCategory, FloorCostBreakdown = floorCostBreakdown, TopExpensiveTasks = topExpensiveTasks, TotalTasks = tasks.Count, TotalMaterials = materials.Count, AverageTaskCost = tasks.Any() ? tasks.Average(t => (t.Material_Cost ?? 0) + (t.Labor_Cost ?? 0)) : 0 }; return View(costAnalysisData); } catch (Exception ex) { TempData["error"] = "Error generating cost analysis: " + ex.Message; System.Diagnostics.Debug.WriteLine($"GenerateCostAnalysisReport Error: {ex.Message}"); return RedirectToAction("CostAnalysisReportSelection"); } } /////////////////////////////////////////////////////////////////////////////////////// // ==================================================================================== // QUICK REPORTS - SCHEDULE REPORT // ==================================================================================== [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ScheduleReportSelection() { var projects = _unitOfWork.ODCProject.GetAll().Select(p => new SelectListItem { Text = p.Name, Value = p.Id.ToString() }); ViewBag.Projects = projects; return View(); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] public IActionResult ScheduleReportSelection(int projectId) { if (projectId == 0) { TempData["error"] = "Please select a project"; return RedirectToAction("ScheduleReportSelection"); } return RedirectToAction("GenerateScheduleReport", new { id = projectId }); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult GenerateScheduleReport(int id) { try { Refresh_Calculations(); var project = _unitOfWork.ODCProject.Get(u => u.Id == id); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("ScheduleReportSelection"); } // Get all project data var floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == id).ToList(); var actions = new List(); var tasks = new List(); foreach (var floor in floors) { var floorActions = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId).ToList(); actions.AddRange(floorActions); } foreach (var action in actions) { var actionTasks = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId).ToList(); tasks.AddRange(actionTasks); } // Get construction sheets for activity tracking var allConstructionSheets = new List(); foreach (var task in tasks) { var sheets = _unitOfWork.Subtask.Where(s => s.TaskId == task.TaskId).ToList(); allConstructionSheets.AddRange(sheets); } // Timeline calculations DateOnly? projectStartDate = null; DateOnly? projectEndDate = null; int totalProjectDuration = 0; var tasksWithDates = tasks.Where(t => t.SDate.HasValue).ToList(); if (tasksWithDates.Any()) { projectStartDate = tasksWithDates.Min(t => t.SDate.Value); foreach (var task in tasksWithDates) { if (task.Duration.HasValue) { var taskEndDate = task.SDate.Value.AddDays(task.Duration.Value); if (!projectEndDate.HasValue || taskEndDate > projectEndDate.Value) { projectEndDate = taskEndDate; } } } if (projectStartDate.HasValue && projectEndDate.HasValue) { totalProjectDuration = projectEndDate.Value.DayNumber - projectStartDate.Value.DayNumber; } } // Task Schedule Details var taskScheduleDetails = new List(); foreach (var task in tasks.Where(t => t.SDate.HasValue)) { var action = actions.FirstOrDefault(a => a.ActionId == task.ActionId); var floor = action != null ? floors.FirstOrDefault(f => f.FloorId == action.FloorId) : null; DateOnly? taskEndDate = null; if (task.Duration.HasValue) { taskEndDate = task.SDate.Value.AddDays(task.Duration.Value); } // Calculate days elapsed int daysElapsed = 0; int daysRemaining = 0; var today = DateOnly.FromDateTime(DateTime.Today); if (task.SDate.HasValue) { if (today >= task.SDate.Value) { daysElapsed = today.DayNumber - task.SDate.Value.DayNumber; } if (taskEndDate.HasValue && today < taskEndDate.Value) { daysRemaining = taskEndDate.Value.DayNumber - today.DayNumber; } } // Determine status string status = "Not Started"; if (task.Completion == 100) { status = "Completed"; } else if (task.Completion > 0) { status = "In Progress"; } else if (taskEndDate.HasValue && today > taskEndDate.Value) { status = "Overdue"; } taskScheduleDetails.Add(new TaskScheduleItem { TaskName = task.Name, FloorName = floor?.Name ?? "N/A", ActionName = action?.Name ?? "N/A", StartDate = task.SDate.Value, EndDate = taskEndDate, Duration = task.Duration ?? 0, Completion = task.Completion ?? 0, DaysElapsed = daysElapsed, DaysRemaining = daysRemaining, Status = status }); } // Floor-wise Schedule Summary var floorScheduleSummary = new List(); foreach (var floor in floors) { var floorActions = actions.Where(a => a.FloorId == floor.FloorId).ToList(); var floorTasks = new List(); foreach (var action in floorActions) { floorTasks.AddRange(tasks.Where(t => t.ActionId == action.ActionId)); } var floorTasksWithDates = floorTasks.Where(t => t.SDate.HasValue).ToList(); DateOnly? floorStart = floorTasksWithDates.Any() ? floorTasksWithDates.Min(t => t.SDate.Value) : null; DateOnly? floorEnd = null; foreach (var task in floorTasksWithDates) { if (task.Duration.HasValue) { var taskEnd = task.SDate.Value.AddDays(task.Duration.Value); if (!floorEnd.HasValue || taskEnd > floorEnd.Value) { floorEnd = taskEnd; } } } int floorDuration = 0; if (floorStart.HasValue && floorEnd.HasValue) { floorDuration = floorEnd.Value.DayNumber - floorStart.Value.DayNumber; } floorScheduleSummary.Add(new FloorScheduleSummary { FloorName = floor.Name, TotalTasks = floorTasks.Count, StartDate = floorStart, EndDate = floorEnd, Duration = floorDuration, CompletedTasks = floorTasks.Count(t => t.Completion == 100), InProgressTasks = floorTasks.Count(t => t.Completion > 0 && t.Completion < 100), NotStartedTasks = floorTasks.Count(t => t.Completion == 0 || t.Completion == null) }); } // Schedule Performance Metrics var totalTasks = tasks.Count; var completedTasks = tasks.Count(t => t.Completion == 100); var inProgressTasks = tasks.Count(t => t.Completion > 0 && t.Completion < 100); var notStartedTasks = tasks.Count(t => t.Completion == 0 || t.Completion == null); var today2 = DateOnly.FromDateTime(DateTime.Today); var overdueTasks = tasks.Count(t => t.SDate.HasValue && t.Duration.HasValue && t.SDate.Value.AddDays(t.Duration.Value) < today2 && t.Completion < 100); var onScheduleTasks = tasks.Count(t => t.SDate.HasValue && t.Duration.HasValue && t.SDate.Value.AddDays(t.Duration.Value) >= today2 && t.Completion < 100); // Critical Path (longest tasks) var criticalPathTasks = taskScheduleDetails .OrderByDescending(t => t.Duration) .Take(5) .ToList(); // Package data for view var scheduleData = new ScheduleReportData { Project = project, ProjectStartDate = projectStartDate, ProjectEndDate = projectEndDate, TotalProjectDuration = totalProjectDuration, TotalTasks = totalTasks, CompletedTasks = completedTasks, InProgressTasks = inProgressTasks, NotStartedTasks = notStartedTasks, OverdueTasks = overdueTasks, OnScheduleTasks = onScheduleTasks, TaskScheduleDetails = taskScheduleDetails.OrderBy(t => t.StartDate).ToList(), FloorScheduleSummary = floorScheduleSummary, CriticalPathTasks = criticalPathTasks, ScheduleComplianceRate = totalTasks > 0 ? ((completedTasks + onScheduleTasks) * 100.0 / totalTasks) : 0 }; return View(scheduleData); } catch (Exception ex) { TempData["error"] = "Error generating schedule report: " + ex.Message; System.Diagnostics.Debug.WriteLine($"GenerateScheduleReport Error: {ex.Message}"); return RedirectToAction("ScheduleReportSelection"); } } /// /// /////////////////////////////////////////////////////////////////////////////////// /// [Authorize(Roles = "Admin")] public IActionResult AssignAllReferenceIds() { IEnumerable projects = _unitOfWork.ODCProject.GetAll(); IEnumerable floors = new List(); IEnumerable actions = new List(); IEnumerable tasks = new List(); int counter = 1; //////////////////// if (projects.Any()) { foreach (var project in projects) { floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == project.Id); if (floors.Any()) { foreach (var floor in floors) { actions = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId); if (actions.Any()) { foreach (var action in actions) { tasks = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId); if (tasks.Any()) { foreach (var task in tasks) { task.Reference_Id = task.ActionId + "." + counter; _unitOfWork.Task.update(task); _unitOfWork.save(); counter++; } counter = 1; } } } } } } } //////////////////// return RedirectToAction("Index", "ODCProject"); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult Index() { Refresh_Calculations(); var projects = _unitOfWork.ODCProject.GetAll(); /*DataTable dt = new DataTable(); var v = dt.Compute("(2+1+3)/5", "");//////////////////////////////////// System.Diagnostics.Debug.WriteLine(v); *//* Refresh_Calculations();*/ return View(projects); } [Authorize(Roles = "Admin,Employee")] public IActionResult Upsert(int? id) //*Combining Get controller for create and edit { /* Refresh_Calculations();*/ ODCProject oDCProject = new() { }; if (id == null || id == 0) /*means it is Create view*/ { return View(oDCProject); } else //Update/Edit functionality { ODCProject oDCProject1 = _unitOfWork.ODCProject.Get(u => u.Id == id); if (oDCProject1 == null) { return View("Error"); } else { return View(oDCProject1); } } } [Authorize(Roles = "Admin")] public IActionResult DeleteProject(int id) { ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == id); IEnumerable floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == id); foreach (var floor in floors) { IEnumerable action = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId); foreach (var item3 in action) { IEnumerable task = _unitOfWork.Task.Where(n => n.ActionId == item3.ActionId); foreach (var item in task) { IEnumerable material = _unitOfWork.Material.Where(b => b.TaskId == item.TaskId); foreach (var item2 in material) { _unitOfWork.Material.Remove(item2); _unitOfWork.save(); } IEnumerable property = _unitOfWork.Property.Where(b => b.TaskId == item.TaskId); foreach (var item4 in property) { _unitOfWork.Property.Remove(item4); _unitOfWork.save(); } _unitOfWork.Task.Remove(item); _unitOfWork.save(); } _unitOfWork.Action.Remove(item3); _unitOfWork.save(); } _unitOfWork.Floor.Remove(floor); _unitOfWork.save(); } _unitOfWork.ODCProject.Remove(project); _unitOfWork.save(); TempData["success"] = "Project deleted successfully"; return RedirectToAction("Index", "ODCProject"); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult Upsert(ODCProject obj) //*Combining Post controller for create and edit { if (ModelState.IsValid) { if (obj.Id == 0) { // For new projects, don't validate dates since they're not shown in the create form obj.P_Material_Cost = 0; _unitOfWork.ODCProject.Add(obj); _unitOfWork.save(); TempData["success"] = "Project created successfully"; return RedirectToAction("Index", "ODCProject"); } else { // For existing projects, validate dates if (obj.EndDate.HasValue && obj.StartDate.HasValue && obj.EndDate < obj.StartDate) { TempData["error"] = "End Date should be greater than Start Date"; return View(obj); } _unitOfWork.ODCProject.Update(obj); _unitOfWork.save(); TempData["success"] = "Project updated successfully"; return RedirectToAction("Index", "ODCProject"); } } else { TempData["error"] = "Please correct the errors below"; return View(obj); } } [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult InitiateFloor(int id) { try { // ISSUE 1: Project ID validation if (id <= 0) { TempData["error"] = "Invalid project ID"; return RedirectToAction("Index", "ODCProject"); } // ISSUE 2: Check if project exists FIRST ODCProject oDCProject = _unitOfWork.ODCProject.Get(u => u.Id == id); if (oDCProject == null) { TempData["error"] = "Project not found"; return RedirectToAction("Index", "ODCProject"); } // ISSUE 3: Initialize Floor object properly Floor floor = new Floor { ODCProjectId = id // Make sure this is set }; ViewBag.ProjectID = id; ViewBag.model1 = oDCProject; // ISSUE 4: Safe DataFloor retrieval with null checks try { List floors = _unitOfWork.DataFloor.GetAll()?.ToList() ?? new List(); List floor_names = new List(); foreach (var item in floors) { // ISSUE 5: Handle null or empty floor names if (item != null && !string.IsNullOrWhiteSpace(item.Name)) { floor_names.Add(item.Name.Trim()); } } // ISSUE 6: Ensure at least some floor options exist if (!floor_names.Any()) { // Add default floor names if none exist floor_names.AddRange(new[] { "Ground Floor", "First Floor", "Second Floor", "Basement" }); } ViewBag.FloorNames = floor_names; } catch (Exception ex) { // ISSUE 7: Handle DataFloor retrieval errors System.Diagnostics.Debug.WriteLine($"Error retrieving DataFloors: {ex.Message}"); // Provide default floor names as fallback ViewBag.FloorNames = new List { "Ground Floor", "First Floor", "Second Floor" }; } return View(floor); } catch (Exception ex) { // ISSUE 8: Catch any unexpected errors System.Diagnostics.Debug.WriteLine($"InitiateFloor GET Error: {ex.Message}"); TempData["error"] = "Error loading floor form. Please try again."; return RedirectToAction("Index", "ODCProject"); } } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult InitiateFloor(Floor obj) { try { if (ModelState.IsValid) { // Validate project exists if (obj.ODCProjectId <= 0) { TempData["error"] = "Invalid project ID"; return RedirectToAction("Index", "ODCProject"); } ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == obj.ODCProjectId); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("Index", "ODCProject"); } obj.F_Material_Cost = 0; bool checker = false; // Check for duplicate floors in the same project IEnumerable existingFloors = _unitOfWork.Floor.Where(u => u.ODCProjectId == obj.ODCProjectId); foreach (var item in existingFloors) { if (item.ODCProjectId == obj.ODCProjectId && !string.IsNullOrWhiteSpace(item.Name) && !string.IsNullOrWhiteSpace(obj.Name) && item.Name.Trim().Equals(obj.Name.Trim(), StringComparison.OrdinalIgnoreCase)) { checker = true; break; } } if (checker == false) { try { _unitOfWork.Floor.Add(obj); _unitOfWork.save(); TempData["success"] = "Floor added successfully"; return RedirectToAction("Details", "ODCProject", new { id = obj.ODCProjectId }); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error saving floor: {ex.Message}"); TempData["error"] = "Error saving floor. Please try again."; // Repopulate ViewBag and return to form RepopulateViewBagData(obj.ODCProjectId); return View(obj); } } else { TempData["error"] = "Floor with this name already exists in this project"; // Repopulate ViewBag and return to form RepopulateViewBagData(obj.ODCProjectId); return View(obj); } } else { TempData["error"] = "Please correct the errors below"; // Repopulate ViewBag and return to form RepopulateViewBagData(obj.ODCProjectId); return View(obj); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"InitiateFloor POST Error: {ex.Message}"); TempData["error"] = "An error occurred. Please try again."; if (obj?.ODCProjectId > 0) { return RedirectToAction("Details", "ODCProject", new { id = obj.ODCProjectId }); } else { return RedirectToAction("Index", "ODCProject"); } } } // Helper method to repopulate ViewBag data (avoid code duplication) private void RepopulateViewBagData(int projectId) { try { ViewBag.ProjectID = projectId; // Get project info ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == projectId); ViewBag.model1 = project; // Get floor names (same logic as GET method) try { List floors = _unitOfWork.DataFloor.GetAll()?.ToList() ?? new List(); List floor_names = new List(); foreach (var item in floors) { if (item != null && !string.IsNullOrWhiteSpace(item.Name)) { floor_names.Add(item.Name.Trim()); } } // Ensure at least some floor options exist if (!floor_names.Any()) { floor_names.AddRange(new[] { "Ground Floor", "First Floor", "Second Floor", "Basement" }); } ViewBag.FloorNames = floor_names; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error retrieving DataFloors in POST: {ex.Message}"); // Provide default floor names as fallback ViewBag.FloorNames = new List { "Ground Floor", "First Floor", "Second Floor" }; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error repopulating ViewBag: {ex.Message}"); // Set minimal ViewBag data to prevent view errors ViewBag.ProjectID = projectId; ViewBag.FloorNames = new List { "Ground Floor", "First Floor", "Second Floor" }; ViewBag.model1 = null; } } static List floorr_global = new List(); [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult Details(int id) { Refresh_Calculations(); ODCProject projectt = _unitOfWork.ODCProject.Get(u => u.Id == id); ViewBag.ODCProject = projectt; floorr_global = _unitOfWork.Floor.Where(u => u.ODCProjectId == id).ToList(); List floorr = new List(); floorr = floorr_global; ViewBag.ProjectIDD = id; return View(floorr); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult GetFloors() { return Json(new { data = floorr_global }); } [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult InitiateAction(int id) { /* Refresh_Calculations();*/ FloorAction action = new() { }; ViewBag.FloorID = id; List actions = _unitOfWork.DataAction.GetAll().ToList(); List action_names = new List(); foreach (var item in actions) { action_names.Add(item.Name); } ViewBag.ActionNames = action_names; Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == id); ViewBag.model1 = floor; return View(action); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult InitiateAction(FloorAction obj) { /* Refresh_Calculations();*/ if (ModelState.IsValid) { obj.A_Material_Cost = 0; bool checker = false; bool checker2 = false; IEnumerable action = _unitOfWork.Action.GetAll(); foreach (var item in action) { if (item.FloorId == obj.FloorId && item.Name == obj.Name) { checker = true; foreach (var item2 in action) { if (item2.FloorId == obj.FloorId && item2.Name == obj.Name + " 1") { checker2 = true; } } } } if (checker == false) { _unitOfWork.Action.Add(obj); _unitOfWork.save(); } else if (checker == true) { string tempi = obj.Name; obj.Name = obj.Name + " 1"; if (checker2 == true) { obj.Name = tempi + " 2"; } _unitOfWork.Action.Add(obj); _unitOfWork.save(); } TempData["success"] = "Action added successfully"; return RedirectToAction("FloorDetails", "ODCProject", new { id = obj.FloorId }); } else { TempData["error"] = "Something wrong"; return View(obj); } } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult FloorDetails(int id) { Refresh_Calculations(); Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == id); List tasks = new List(); IEnumerable actions = _unitOfWork.Action.Where(u => u.FloorId == id); if (actions != null) { foreach (var itemm in actions) { List taskss = _unitOfWork.Task.Where(u => u.ActionId == itemm.ActionId).ToList(); tasks.AddRange(taskss); } } double? labor_T = 0; double? mat_T = 0; double? total = 0; if (tasks != null) { foreach (var item in tasks) { labor_T = labor_T + item.Labor_Cost; mat_T = mat_T + item.Material_Cost; total = labor_T + mat_T; } } /* if (total != (labor_T + mat_T)) { TempData["error"] = "Issue in calculation"; } else { TempData["success"] = "Data imported"; }*/ ViewBag.labor = labor_T; ViewBag.material = mat_T; ViewBag.total = total; ODCProject projectt = _unitOfWork.ODCProject.Get(u => u.Id == floor.ODCProjectId); ViewBag.Project = projectt; ViewBag.Floor = floor; IEnumerable action = _unitOfWork.Action.GetAll(); ViewBag.FloorIDD = id; return View(action); } [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult InitiateTask(int id) { /* Refresh_Calculations();*/ ActionTask task = new() { SDate = new DateOnly(2024, 8, 1), Duration = 5 }; ViewBag.ActionID = id; List tasks = _unitOfWork.DataTask.GetAll().ToList(); if (tasks == null) { return View("Error"); } else { List task_names = new List(); foreach (var item in tasks) { task_names.Add(item.Name); } ViewBag.TaskNames = task_names; FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == id); ViewBag.model1 = action; return View(task); } } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult InitiateTask(ActionTask obj) { if (ModelState.IsValid) { obj.Material_Cost = 0; obj.Completion = 0; // Get the DataTask template for labor rate DataTask taskTemplate = _unitOfWork.DataTask.Get(u => u.Name == obj.Name); if (taskTemplate != null && taskTemplate.Labor_Rate != null) { obj.Labor_Rate = (double)taskTemplate.Labor_Rate; } else { obj.Labor_Rate = 0; } obj.Labor_Cost = 0; obj.Measured_Work = 0; obj.Mat_Lab = 0; bool checker = false; IEnumerable task = _unitOfWork.Task.GetAll(); foreach (var item in task) { if (item.ActionId == obj.ActionId && item.Name == obj.Name) { checker = true; } } if (checker == false) { // Add the task first _unitOfWork.Task.Add(obj); _unitOfWork.save(); // Get the newly created task ID int newTaskId = obj.TaskId; // Auto-fetch template properties and materials bool templateDataAdded = AutoFetchTaskTemplate(newTaskId, obj.Name, obj.ActionId); if (templateDataAdded) { TempData["success"] = "Task added successfully with template data. Please fill in the property values."; // Get project ID for redirection to Fill Properties page FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == obj.ActionId); Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == action.FloorId); int projectId = floor.ODCProjectId; // Set flag to indicate template was loaded DataTemp tempFlag = _unitOfWork.DataTemp.Get(u => u.DataTempId == 1); if (tempFlag != null) { tempFlag.Value = 1; _unitOfWork.DataTemp.update(tempFlag); _unitOfWork.save(); } // Redirect to Fill Properties page (assuming this is the Calculation controller's EnterCompleteData action) return RedirectToAction("EnterCompleteData", "Calculation", new { id = projectId }); } else { TempData["success"] = "Task added successfully. No template found - please add properties and materials manually."; return RedirectToAction("ActionDetails", "ODCProject", new { id = obj.ActionId }); } } else { TempData["error"] = "Task already present! Go to 'fill properties' to duplicate"; return RedirectToAction("ActionDetails", "ODCProject", new { id = obj.ActionId }); } } else { TempData["error"] = "Something wrong"; return View(obj); } } // Helper method to auto-fetch task template private bool AutoFetchTaskTemplate(int taskId, string taskName, int actionId) { try { // Get the action and floor details for material creation FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == actionId); Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == action.FloorId); ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == floor.ODCProjectId); bool dataAdded = false; // Get DataTask ID using the same logic as your existing PopulateTask method DataTask dataTask = _unitOfWork.DataTask.Get(u => u.Name == taskName); if (dataTask == null) { return false; // No template found } int dataTaskId = dataTask.DataTaskId; // Fetch template properties based on DataTaskId (following your existing pattern) IEnumerable templateProperties = _unitOfWork.TemplateProperty.Where(u => u.DataTaskId == dataTaskId); if (templateProperties != null && templateProperties.Any()) { // Check for existing properties to avoid duplication IEnumerable existingProperties = _unitOfWork.Property.Where(u => u.TaskId == taskId); foreach (var templateProp in templateProperties) { bool propertyExists = false; foreach (var existingProp in existingProperties) { if (existingProp.Name == templateProp.Name) { propertyExists = true; break; } } if (!propertyExists) { // Get description from DataProperty table DataProperty dbProperty = _unitOfWork.DataProperty.Get(u => u.Name == templateProp.Name); string description = "Not given"; if (dbProperty != null && !string.IsNullOrEmpty(dbProperty.Description)) { description = dbProperty.Description; } Property newProperty = new Property() { Name = templateProp.Name, TaskId = taskId, Description = description, Value = 0, // Start with 0, user will fill this Visible = templateProp.Visible }; _unitOfWork.Property.Add(newProperty); dataAdded = true; } } } // Fetch template materials based on DataTaskId (following your existing pattern) IEnumerable templateMaterials = _unitOfWork.TemplateMaterial.Where(u => u.DataTaskId == dataTaskId); if (templateMaterials != null && templateMaterials.Any()) { // Check for existing materials to avoid duplication IEnumerable existingMaterials = _unitOfWork.Material.Where(u => u.TaskId == taskId); foreach (var templateMat in templateMaterials) { bool materialExists = false; foreach (var existingMat in existingMaterials) { if (existingMat.Name == templateMat.Name) { materialExists = true; break; } } if (!materialExists) { // Get the unit cost from DataMaterial (following your existing pattern) DataMaterial dbMaterial = _unitOfWork.DataMaterial.Get(u => u.Name == templateMat.Name); double? unitCost = 0; if (dbMaterial != null) { unitCost = dbMaterial.Unit_Cost; } Material newMaterial = new Material() { Name = templateMat.Name, ODCProject = project, ODCProjectId = project.Id, TaskId = taskId, ActionId = actionId, FloorId = floor.FloorId, Qty = 0, // Start with 0, user will fill this Unit = "Not given", // TemplateMaterial doesn't have Unit property, so use default Unit_Cost = unitCost }; _unitOfWork.Material.Add(newMaterial); dataAdded = true; } } } if (dataAdded) { _unitOfWork.save(); } return dataAdded; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error in AutoFetchTaskTemplate: {ex.Message}"); return false; } } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ActionDetails(int id) { Refresh_Calculations(); IEnumerable tasks = _unitOfWork.Task.Where(u => u.ActionId == id); double? labor_T = 0; double? mat_T = 0; double? total = 0; if (tasks != null) { foreach (var item in tasks) { labor_T = labor_T + item.Labor_Cost; mat_T = mat_T + item.Material_Cost; total = total + item.Mat_Lab; } } /*if (total != (labor_T + mat_T)) { TempData["error"] = "Issue in calculation"; } else { TempData["success"] = "Data imported"; }*/ ViewBag.labor = labor_T; ViewBag.material = mat_T; ViewBag.total = total; FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == id); ViewBag.Action = action; IEnumerable task = _unitOfWork.Task.GetAll(); ViewBag.ActionIDD = id; Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == action.FloorId); ViewBag.floor = floor; ODCProject project = _unitOfWork.ODCProject.Get(i => i.Id == floor.ODCProjectId); ViewBag.project = project; return View(task); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult CompleteDetail(int id) { Refresh_Calculations(); ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == id); IEnumerable floors = _unitOfWork.Floor.GetAll(); IEnumerable actions = _unitOfWork.Action.GetAll(); IEnumerable tasks = _unitOfWork.Task.GetAll(); IEnumerable properties = _unitOfWork.Property.GetAll(); dynamic mymodel = new ExpandoObject(); mymodel.floors = floors; mymodel.project = project; mymodel.actions = actions; mymodel.tasks = tasks; mymodel.properties = properties.Where(u => u.Visible == true); return View(mymodel); } void Refresh_Calculations() { ///////////////////// IEnumerable task = _unitOfWork.Task.GetAll(); if (task != null) { foreach (var tas in task) { tas.Material_Cost = 0; var materials = _unitOfWork.Material.GetAll(); if (materials != null) { foreach (var mat in materials) { if (tas.TaskId == mat.TaskId) { tas.Material_Cost = tas.Material_Cost + (mat.Unit_Cost * mat.Qty); } } } _unitOfWork.Task.update(tas); _unitOfWork.save(); } } ///////////////////// ///////////////////// IEnumerable action = _unitOfWork.Action.GetAll(); if (action != null) { foreach (var act in action) { act.A_Material_Cost = 0; var tasks = _unitOfWork.Task.GetAll(); if (tasks != null) { foreach (var tas in tasks) { if (tas.ActionId == act.ActionId) { act.A_Material_Cost = act.A_Material_Cost + tas.Material_Cost; } } } _unitOfWork.Action.update(act); _unitOfWork.save(); } } ///////////////////// IEnumerable floorr = _unitOfWork.Floor.GetAll(); if (floorr != null) { foreach (var flo in floorr) { flo.F_Material_Cost = 0; var actions = _unitOfWork.Action.GetAll(); if (actions != null) { foreach (var act in actions) { if (act.FloorId == flo.FloorId) { flo.F_Material_Cost = flo.F_Material_Cost + act.A_Material_Cost; } } } _unitOfWork.Floor.update(flo); _unitOfWork.save(); } } var projects = _unitOfWork.ODCProject.GetAll(); ///////////////////// if (projects != null) { foreach (var pro in projects) { pro.P_Material_Cost = 0; var floors = _unitOfWork.Floor.GetAll(); if (floors != null) { foreach (var flo in floors) { if (flo.ODCProjectId == pro.Id) { pro.P_Material_Cost = pro.P_Material_Cost + flo.F_Material_Cost; } } } _unitOfWork.ODCProject.Update(pro); _unitOfWork.save(); } } ///////////////////// IEnumerable etasks = _unitOfWork.ScheduleTask.Where(u => u.IsEndDate == true); if (etasks != null) { foreach (var etask in etasks) { ActionTask taskk = _unitOfWork.Task.Get(u => u.TaskId == etask.TaskId); if (taskk != null) { etask.Completion = taskk.Completion; TimeOnly SEtimeOnly = new TimeOnly(9, 00, 00); DateOnly SEdateOnly = ((DateOnly)taskk.SDate).AddDays((int)taskk.Duration); var SEdateTime = SEdateOnly.ToDateTime(SEtimeOnly); TimeOnly EtimeOnly = new TimeOnly(17, 00, 00); DateOnly EdateOnly = ((DateOnly)taskk.SDate).AddDays((int)taskk.Duration); var EdateTime = EdateOnly.ToDateTime(EtimeOnly); etask.SDate = SEdateTime; etask.EDate = EdateTime; etask.Name = taskk.Name; if (etask.Completion == 100) { _unitOfWork.ScheduleTask.Remove(etask); _unitOfWork.save(); } else { _unitOfWork.ScheduleTask.update(etask); _unitOfWork.save(); } } } } ///////////////////////////// ///////////////////// IEnumerable stasks = _unitOfWork.ScheduleTask.Where(u => u.IsEndDate == false); if (stasks != null) { foreach (var stask in stasks) { ActionTask taskk = _unitOfWork.Task.Get(u => u.TaskId == stask.TaskId); if (taskk != null) { stask.Completion = taskk.Completion; TimeOnly SStimeOnly = new TimeOnly(09, 00, 00); DateOnly SSdateOnly = (DateOnly)taskk.SDate; var SSdateTime = SSdateOnly.ToDateTime(SStimeOnly); TimeOnly EtimeOnly = new TimeOnly(17, 00, 00); DateOnly EdateOnly = (DateOnly)taskk.SDate; var EdateTime = EdateOnly.ToDateTime(EtimeOnly); stask.SDate = SSdateTime; stask.EDate = EdateTime; stask.Name = taskk.Name; if (stask.Completion == 100) { _unitOfWork.ScheduleTask.Remove(stask); _unitOfWork.save(); } else { _unitOfWork.ScheduleTask.update(stask); _unitOfWork.save(); } } } } } [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult EditTask(int? id) { ActionTask task = _unitOfWork.Task.Get(u => u.TaskId == id); return View(task); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult EditTask(ActionTask task) { if (ModelState.IsValid) { task.Labor_Cost = task.Measured_Work * task.Labor_Rate; task.Mat_Lab = task.Labor_Cost + task.Material_Cost; _unitOfWork.Task.update(task); _unitOfWork.save(); TempData["success"] = "Task edited successfully"; return RedirectToAction("TaskDetails", "ODCProject", new { id = task.TaskId }); } else { TempData["error"] = "Something Wrong"; return View(task); } } [Authorize(Roles = "Admin")] public IActionResult DeleteTask(int? id) { /* Refresh_Calculations();*/ ActionTask task = _unitOfWork.Task.Get(u => u.TaskId == id); IEnumerable material = _unitOfWork.Material.Where(v => v.TaskId == task.TaskId); foreach (var item in material) { _unitOfWork.Material.Remove(item); _unitOfWork.save(); } IEnumerable property = _unitOfWork.Property.Where(b => b.TaskId == task.TaskId); foreach (var item4 in property) { _unitOfWork.Property.Remove(item4); _unitOfWork.save(); } _unitOfWork.Task.Remove(task); _unitOfWork.save(); TempData["success"] = "Task deleted successfully"; return RedirectToAction("ActionDetails", "ODCProject", new { id = task.ActionId }); } [Authorize(Roles = "Admin")] public IActionResult DeleteFloor(int? id) { /* Refresh_Calculations();*/ Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == id); IEnumerable action = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId); foreach (var item3 in action) { IEnumerable task = _unitOfWork.Task.Where(n => n.ActionId == item3.ActionId); foreach (var item in task) { IEnumerable material = _unitOfWork.Material.Where(b => b.TaskId == item.TaskId); foreach (var item2 in material) { _unitOfWork.Material.Remove(item2); _unitOfWork.save(); } IEnumerable property = _unitOfWork.Property.Where(b => b.TaskId == item.TaskId); foreach (var item4 in property) { _unitOfWork.Property.Remove(item4); _unitOfWork.save(); } _unitOfWork.Task.Remove(item); _unitOfWork.save(); } _unitOfWork.Action.Remove(item3); _unitOfWork.save(); } _unitOfWork.Floor.Remove(floor); _unitOfWork.save(); TempData["success"] = "Floor deleted successfully"; return RedirectToAction("Details", "ODCProject", new { id = floor.ODCProjectId }); } [Authorize(Roles = "Admin")] public IActionResult DeleteAction(int? id) { /* Refresh_Calculations();*/ FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == id); IEnumerable task = _unitOfWork.Task.Where(n => n.ActionId == action.ActionId); foreach (var item in task) { IEnumerable material = _unitOfWork.Material.Where(b => b.TaskId == item.TaskId); foreach (var item2 in material) { _unitOfWork.Material.Remove(item2); _unitOfWork.save(); } IEnumerable property = _unitOfWork.Property.Where(v => v.TaskId == item.TaskId); foreach (var item3 in property) { _unitOfWork.Property.Remove(item3); _unitOfWork.save(); } _unitOfWork.Task.Remove(item); _unitOfWork.save(); } _unitOfWork.Action.Remove(action); _unitOfWork.save(); TempData["success"] = "Action deleted successfully"; return RedirectToAction("FloorDetails", "ODCProject", new { id = action.FloorId }); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult TaskDetails(int? id) { Refresh_Calculations(); ActionTask task = _unitOfWork.Task.Get(u => u.TaskId == id); if (task.Labor_Cost != null && task.Material_Cost != null) { task.Mat_Lab = task.Labor_Cost + task.Material_Cost; _unitOfWork.Task.update(task); _unitOfWork.save(); } FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == task.ActionId); Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == action.FloorId); ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == floor.ODCProjectId); IEnumerable material = _unitOfWork.Material.Where(u => u.TaskId == id); IEnumerable property = _unitOfWork.Property.Where(b => b.TaskId == id); dynamic mymodel = new ExpandoObject(); mymodel.task = task; mymodel.floor = floor; mymodel.action = action; mymodel.project = project; mymodel.materials = material; mymodel.properties = property; return View(mymodel); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult Scheduale_of_Action(int? id) { IEnumerable tasks = _unitOfWork.Task.Where(u => u.ActionId == id); DateOnly? StartDate = new DateOnly(2030, 12, 1); DateOnly? EndDate = new DateOnly(2022, 4, 1); DateOnly? Task_EndDate = new DateOnly(); foreach (var item in tasks) { if (item.SDate.HasValue) { Task_EndDate = item.SDate.Value.AddDays((int)item.Duration); } else { item.SDate = new DateOnly(2024, 10, 10); item.Duration = 10; Task_EndDate = item.SDate.Value.AddDays((int)item.Duration); } if (item.SDate < StartDate) { StartDate = item.SDate.Value.AddDays(-2); } if (Task_EndDate > EndDate) { EndDate = Task_EndDate.Value.AddDays(15); } } ViewBag.StartDate = StartDate; ViewBag.EndDate = EndDate; int f_id = _unitOfWork.Action.Get(u => u.ActionId == id).FloorId; ViewBag.F_Id = f_id; return View(tasks); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult Find_Task_Work(int id) { ActionTask task = _unitOfWork.Task.Get(u => u.TaskId == id); FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == task.ActionId); Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == action.FloorId); ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == floor.ODCProjectId); dynamic mymodel = new ExpandoObject(); mymodel.task = task; mymodel.floor = floor; mymodel.action = action; mymodel.project = project; return View(mymodel); } [Authorize(Roles = "Admin,Employee,Customer")] public IActionResult DuplicateTask(int id) { ActionTask task = _unitOfWork.Task.Get(u => u.TaskId == id); string last_c = task.Name[task.Name.Length - 1].ToString(); int new_c = 0; string new_name = ""; if (int.TryParse(last_c, out _) == false) { new_c = 0; new_name = task.Name.Substring(0, task.Name.Length) + " " + new_c; string new_temp = new_name.Substring(0, task.Name.Length - 2); IEnumerable tasks = _unitOfWork.Task.Where(u => u.Name.Substring(0, task.Name.Length - 2) == new_name.Substring(0, task.Name.Length - 2)).Where(u => u.ActionId == task.ActionId); foreach (var check in tasks) { new_c = new_c + 1; } new_name = task.Name.Substring(0, task.Name.Length) + " " + new_c; } else { /*new_c = int.Parse(last_c) + 1;*/ new_name = task.Name.Substring(0, task.Name.Length - 1) + new_c; IEnumerable tasks = _unitOfWork.Task.Where(u => u.Name.Substring(0, task.Name.Length - 2) == new_name.Substring(0, task.Name.Length - 2)).Where(u => u.ActionId == task.ActionId); foreach (var check in tasks) { new_c = new_c + 1; } new_name = task.Name.Substring(0, task.Name.Length - 1) + new_c; } ActionTask ntask = new ActionTask() { Name = new_name, ActionId = task.ActionId, Material_Cost = 0, Labor_Rate = task.Labor_Rate, Labor_Cost = 0, Measured_Work = 0, Mat_Lab = 0, Completion = 0, SDate = new DateOnly(2024, 5, 1), Duration = task.Duration }; _unitOfWork.Task.Add(ntask); _unitOfWork.save(); FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == task.ActionId); int temp = _unitOfWork.Floor.Get(u => u.FloorId == action.FloorId).ODCProjectId; IEnumerable properties = _unitOfWork.Property.Where(u => u.TaskId == task.TaskId); IEnumerable old_properties = properties; foreach (var item in old_properties) { Property new_property = new Property() { Name = item.Name, TaskId = ntask.TaskId, Description = item.Description, Value = 0, Visible = item.Visible }; _unitOfWork.Property.Add(new_property); _unitOfWork.save(); } IEnumerable materials = _unitOfWork.Material.Where(u => u.TaskId == task.TaskId); IEnumerable old_materials = materials; foreach (var item in old_materials) { Material new_material = new Material() { Name = item.Name, ODCProject = item.ODCProject, ODCProjectId = item.ODCProjectId, TaskId = ntask.TaskId, ActionId = item.ActionId, FloorId = item.FloorId, Qty = 0, Unit = item.Unit, Unit_Cost = item.Unit_Cost }; _unitOfWork.Material.Add(new_material); _unitOfWork.save(); } DataTemp test = _unitOfWork.DataTemp.Get(u => u.DataTempId == 1); test.Value = 1; _unitOfWork.DataTemp.update(test); _unitOfWork.save(); return RedirectToAction("EnterCompleteData", "Calculation", new { id = temp }); } [Authorize(Roles = "Admin,Employee,Customer")] public IActionResult AddTaskToScheduler(int? id) { ActionTask task = _unitOfWork.Task.Get(u => u.TaskId == id); if (task != null) { FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == task.ActionId); if (action != null) { Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == action.FloorId); if (floor != null) { try { if (task.Completion == 100) { TempData["error"] = "Task is already completed!"; } else { ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == floor.ODCProjectId); TimeOnly StimeOnly = new TimeOnly(09, 00, 00); DateOnly SdateOnly = (DateOnly)task.SDate; var SdateTime = SdateOnly.ToDateTime(StimeOnly); ScheduleTask SscheduleTask = new ScheduleTask() { TaskId = task.TaskId, Name = task.Name, ActionId = task.ActionId, FloorId = action.FloorId, ODCProjectId = floor.ODCProjectId, SDate = SdateTime, EDate = SdateTime.AddHours(8), Details = "Starting Time for Task " + action.Name + " " + floor.Name + " " + project.Name, IsAllDay = false, IsReadonly = true, IsEndDate = false, Completion = task.Completion, BColor = "#36802d" }; _unitOfWork.ScheduleTask.Add(SscheduleTask); _unitOfWork.save(); TimeOnly EtimeOnly = new TimeOnly(09, 00, 00); DateOnly EdateOnly = ((DateOnly)task.SDate).AddDays((int)task.Duration); var EdateTime = EdateOnly.ToDateTime(EtimeOnly); ScheduleTask EscheduleTask = new ScheduleTask() { TaskId = task.TaskId, Name = task.Name, ActionId = task.ActionId, FloorId = action.FloorId, ODCProjectId = floor.ODCProjectId, SDate = EdateTime, EDate = EdateTime.AddHours(8), Details = "Ending Time for Task " + action.Name + " " + floor.Name + " " + project.Name, IsAllDay = false, IsReadonly = true, IsEndDate = true, Completion = task.Completion, BColor = "#fc3468" }; _unitOfWork.ScheduleTask.Add(EscheduleTask); _unitOfWork.save(); TempData["success"] = "Reminder addedd!"; } } catch (Exception e) { TempData["error"] = "Something wrong!"; //do nothing } } } } return RedirectToAction("TaskDetails", "ODCProject", new { id }); } [Authorize(Roles = "Admin,Employee,Customer")] public IActionResult RemoveTaskFromScheduler(int? id) { IEnumerable stasks = _unitOfWork.ScheduleTask.Where(u => u.TaskId == id); if (stasks != null) { foreach (var item in stasks) { _unitOfWork.ScheduleTask.Remove(item); _unitOfWork.save(); TempData["success"] = "Reminder Removed!"; } } else { TempData["error"] = "No Reminder present!"; } return RedirectToAction("TaskDetails", "ODCProject", new { id }); } [Authorize(Roles = "Admin,Employee,Customer")] public IActionResult DisplayScheduler(int? id) { Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == id); if (floor != null) { ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == floor.ODCProjectId); if (project != null) { IEnumerable stasks = _unitOfWork.ScheduleTask.Where(u => u.FloorId == id); if (stasks != null) { ViewBag.Floor = floor; ViewBag.Project = project; TempData["success"] = "Scheduler updated!"; return View(stasks); } else { TempData["error"] = "No scheduled tasks found!"; return RedirectToAction("FloorDetails", "ODCProject", new { id }); } } else { TempData["error"] = "No project in database"; return RedirectToAction("FloorDetails", "ODCProject", new { id }); } } else { TempData["error"] = "No floor found!"; return RedirectToAction("FloorDetails", "ODCProject", new { id }); } } [Authorize(Roles = "Admin,Employee,Customer")] public IActionResult CreateST(int? id) { ViewBag.TaskId = id; ActionTask task = _unitOfWork.Task.Get(u => u.TaskId == id); FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == task.ActionId); Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == action.FloorId); ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == floor.ODCProjectId); ViewBag.ProjectId = project.Id; var now = DateTime.Now; // Round to current hour and minute, removing seconds and milliseconds var currentTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0); Subtask subtask = new() { StartTimeStamp = currentTime, EndTimeStamp = currentTime.AddDays(2) }; return View(subtask); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] [ValidateAntiForgeryToken] public async Task CreateST(Subtask obj) { if (ModelState.IsValid) { if (obj.EndTimeStamp > obj.StartTimeStamp) { if (obj.FileUpload != null) { using (var memoryStream = new MemoryStream()) { await obj.FileUpload.CopyToAsync(memoryStream); obj.FileData = memoryStream.ToArray(); obj.FileContentType = obj.FileUpload.ContentType; obj.FileName = obj.FileUpload.FileName; } } _unitOfWork.Subtask.Add(obj); _unitOfWork.save(); TempData["success"] = "Subtask created successfully"; return RedirectToAction("TaskDetails", "ODCProject", new { id = obj.TaskId }); } else { ModelState.AddModelError("EndTimeStamp", "Ending Timestamp must be greater than Starting Timestamp"); return View(obj); } } return View(obj); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult SubTaskList(int id) { IEnumerable stasks = _unitOfWork.Subtask.Where(u => u.TaskId == id); if (stasks != null && stasks.Count() != 0) { ViewBag.TaskId = id; return View(stasks); } else { TempData["error"] = "No Construction Sheet Available!"; return RedirectToAction("CreateST", "ODCProject", new { id }); } } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult DownloadFile(int id) { var subtask = _unitOfWork.Subtask.Get(u => u.SubtaskId == id); if (subtask == null || subtask.FileData == null) { TempData["error"] = "File not found!"; return RedirectToAction("SubTaskList", new { id = subtask?.TaskId }); } return File(subtask.FileData, subtask.FileContentType, subtask.FileName); } [Authorize(Roles = "Admin,Employee,Customer")] public IActionResult EditST(int? id) { if (id == null) { return NotFound(); } Subtask subtask = _unitOfWork.Subtask.Get(u => u.SubtaskId == id); if (subtask == null) { return NotFound(); } return View(subtask); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] [ValidateAntiForgeryToken] public async Task EditST(Subtask obj) { if (ModelState.IsValid) { if (obj.EndTimeStamp > obj.StartTimeStamp) { try { // Get existing subtask var existingSubtask = _unitOfWork.Subtask.Get(u => u.SubtaskId == obj.SubtaskId); if (existingSubtask == null) { return NotFound(); } // Update file if new one is uploaded if (obj.FileUpload != null) { using (var memoryStream = new MemoryStream()) { await obj.FileUpload.CopyToAsync(memoryStream); obj.FileData = memoryStream.ToArray(); obj.FileContentType = obj.FileUpload.ContentType; obj.FileName = obj.FileUpload.FileName; } } else { // Preserve existing file data if no new file is uploaded obj.FileData = existingSubtask.FileData; obj.FileContentType = existingSubtask.FileContentType; obj.FileName = existingSubtask.FileName; } // Copy updated values to existing entity existingSubtask.StartTimeStamp = obj.StartTimeStamp; existingSubtask.EndTimeStamp = obj.EndTimeStamp; existingSubtask.FileData = obj.FileData; existingSubtask.FileContentType = obj.FileContentType; existingSubtask.FileName = obj.FileName; // Copy any other properties that need to be updated _unitOfWork.Subtask.update(existingSubtask); _unitOfWork.save(); TempData["success"] = "Subtask updated successfully"; return RedirectToAction("TaskDetails", "ODCProject", new { id = obj.TaskId }); } catch (Exception ex) { ModelState.AddModelError("", "Error updating subtask: " + ex.Message); return View(obj); } } else { ModelState.AddModelError("EndTimeStamp", "Ending Timestamp must be greater than Starting Timestamp"); return View(obj); } } return View(obj); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult TBudgetView(int id) { ActionTask task = _unitOfWork.Task.Get(u => u.TaskId == id); if (task == null) { return NotFound(); } // Use reflection to find and set all double? properties that are null or have a value other than 0.0 var properties = typeof(ActionTask).GetProperties() .Where(p => p.PropertyType == typeof(double?)); foreach (var property in properties) { var value = property.GetValue(task) as double?; if (value == null) { property.SetValue(task, 0.0); } } // Save the changes to the database _unitOfWork.Task.update(task); _unitOfWork.save(); return View(task); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult TBudgetEdit(int id) { ActionTask task = _unitOfWork.Task.Get(u => u.TaskId == id); if (task == null) { return NotFound(); } FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == task.ActionId); Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == action.FloorId); ViewBag.ProjectId = floor.ODCProjectId; return View(task); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] [ValidateAntiForgeryToken] public IActionResult TBudgetEdit(ActionTask actionTask) { if (ModelState.IsValid) { try { // Get the existing task to preserve other fields var existingTask = _unitOfWork.Task.Get(u => u.TaskId == actionTask.TaskId); if (existingTask == null) { return NotFound(); } // Update only the editable fields existingTask.M_Labor_Cost = actionTask.M_Labor_Cost; existingTask.M_Mat_Cost = actionTask.M_Mat_Cost; existingTask.A_Labor_Cost = actionTask.A_Labor_Cost; existingTask.A_Mat_Cost = actionTask.A_Mat_Cost; // Calculate derived fields existingTask.M_Budget = existingTask.M_Labor_Cost + existingTask.M_Mat_Cost; existingTask.A_Budget_Left = existingTask.M_Budget - existingTask.A_Labor_Cost - existingTask.A_Mat_Cost; existingTask.T_Profit_Loss = existingTask.Mat_Lab - existingTask.A_Labor_Cost - existingTask.A_Mat_Cost; _unitOfWork.Task.update(existingTask); _unitOfWork.save(); FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == actionTask.ActionId); Floor floor = _unitOfWork.Floor.Get(u => u.FloorId == action.FloorId); var pr_id = floor.ODCProjectId; TempData["success"] = "Budget updated successfully"; return RedirectToAction("ProjectBudget", "Calculation", new { id = pr_id }); } catch (Exception ex) { ModelState.AddModelError("", "An error occurred while saving the changes. Please try again."); // Log the exception details Debug.WriteLine($"Error updating task budget: {ex.Message}"); } } return View(actionTask); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ProjectGanttChart(int? id) { ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == id); List floors = new List(); List actions = new List(); List tasks = new List(); floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == id).ToList(); foreach (var floor in floors) { List tempa = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId).ToList(); actions.AddRange(tempa); } foreach (var action in actions) { List tempt = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId).ToList(); tasks.AddRange(tempt); } DateOnly? StartDate = new DateOnly(2030, 12, 1); DateOnly? EndDate = new DateOnly(2022, 4, 1); DateOnly? Task_EndDate = new DateOnly(); if (tasks.Count != 0) { foreach (var item in tasks) { if (item.SDate.HasValue) { Task_EndDate = item.SDate.Value.AddDays((int)item.Duration); } else { item.SDate = new DateOnly(2024, 10, 10); item.Duration = 10; Task_EndDate = item.SDate.Value.AddDays((int)item.Duration); } if (item.SDate < StartDate) { StartDate = item.SDate.Value.AddDays(-2); } if (Task_EndDate > EndDate) { EndDate = Task_EndDate.Value.AddDays(15); } } } ViewBag.StartDate = StartDate; ViewBag.EndDate = EndDate; /* int f_id = _unitOfWork.Action.Get(u => u.ActionId == id).FloorId; ViewBag.F_Id = f_id;*/ ViewBag.Id = id; return View(tasks); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult InteractiveProjectGantt(int id) { Refresh_Calculations(); var project = _unitOfWork.ODCProject.Get(u => u.Id == id); var floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == id).ToList(); var actions = new List(); var tasks = new List(); foreach (var floor in floors) { var floorActions = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId).ToList(); actions.AddRange(floorActions); } foreach (var action in actions) { var actionTasks = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId).ToList(); tasks.AddRange(actionTasks); } // Calculate dates DateOnly? startDate = new DateOnly(2030, 12, 1); DateOnly? endDate = new DateOnly(2022, 4, 1); if (tasks.Any()) { foreach (var task in tasks) { var taskEndDate = task.SDate.HasValue ? task.SDate.Value.AddDays((int)task.Duration) : new DateOnly(2024, 10, 10).AddDays(10); if (!task.SDate.HasValue) { task.SDate = new DateOnly(2024, 10, 10); task.Duration = 10; } if (task.SDate < startDate) { startDate = task.SDate.Value.AddDays(-2); } if (taskEndDate > endDate) { endDate = taskEndDate.AddDays(15); } } } // Prepare hierarchical data var ganttData = new List(); int taskCounter = 1; // Add floors foreach (var floor in floors) { var floorTask = new ExpandoObject() as dynamic; floorTask.TaskId = taskCounter++; floorTask.Name = floor.Name; floorTask.SDate = startDate; floorTask.Duration = 0; floorTask.Completion = 0; floorTask.ParentId = null; floorTask.HasChildren = true; floorTask.IsExpanded = true; ganttData.Add(floorTask); // Add actions for each floor var floorActions = actions.Where(a => a.FloorId == floor.FloorId); foreach (var action in floorActions) { var actionTask = new ExpandoObject() as dynamic; actionTask.TaskId = taskCounter++; actionTask.Name = action.Name; actionTask.SDate = startDate; actionTask.Duration = 0; actionTask.Completion = 0; actionTask.ParentId = floorTask.TaskId; actionTask.HasChildren = true; actionTask.IsExpanded = true; ganttData.Add(actionTask); // Add tasks for each action var actionTasks = tasks.Where(t => t.ActionId == action.ActionId); foreach (var task in actionTasks) { var taskItem = new ExpandoObject() as dynamic; taskItem.TaskId = taskCounter++; taskItem.Name = task.Name; taskItem.SDate = task.SDate ?? new DateOnly(2024, 10, 10); taskItem.Duration = task.Duration; taskItem.Completion = task.Completion; taskItem.ParentId = actionTask.TaskId; taskItem.HasChildren = false; taskItem.IsExpanded = true; ganttData.Add(taskItem); } } } ViewBag.StartDate = startDate; ViewBag.EndDate = endDate; ViewBag.ProjectId = id; ViewBag.ProjectName = project.Name; return View(ganttData); } [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult BudgetP() { IEnumerable ProjectList = _unitOfWork.ODCProject.GetAll().Select(u => new SelectListItem { Text = u.Name, Value = u.Id.ToString() }); ViewBag.ProjectList = ProjectList; return View(); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult BudgetP(int list) { return RedirectToAction("ProjectBudget", "Calculation", new { id = list }); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult ConstructionTaskList() { IEnumerable ProjectList = _unitOfWork.ODCProject.GetAll().Select(u => new SelectListItem { Text = u.Name, Value = u.Id.ToString() }); ViewBag.ProjectList = ProjectList; return View(); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult ConstructionTaskList(int list) { return RedirectToAction("ConstructionTaskListProject", "ODCProject", new { id = list }); } /*[Authorize(Roles = "Admin,Employee,Customer")] [HttpPost]*/ public IActionResult ConstructionTaskListProject(int id) { ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == id); IEnumerable floors = _unitOfWork.Floor.GetAll(); IEnumerable actions = _unitOfWork.Action.GetAll(); IEnumerable tasks = _unitOfWork.Task.GetAll(); IEnumerable properties = _unitOfWork.Property.GetAll(); dynamic mymodel = new ExpandoObject(); mymodel.floors = floors; mymodel.project = project; mymodel.actions = actions; mymodel.tasks = tasks; mymodel.properties = properties.Where(u => u.Visible == true); return View(mymodel); } // Add this action to your ODCProjectController [Authorize(Roles = "Admin,Employee,Customer")] public IActionResult GenerateExcelReport(int id) { try { byte[] fileContent = _reportService.GenerateExcelReport(id); if (fileContent == null) { TempData["error"] = "Could not generate report. Construction sheet not found."; return RedirectToAction("SubTaskList"); } string fileName = $"ConstructionSheet_{id}_Report.xlsx"; return File(fileContent, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName); } catch (Exception ex) { TempData["error"] = "Error generating report: " + ex.Message; return RedirectToAction("SubTaskList", new { id = id }); } } [Authorize(Roles = "Admin,Employee")] public IActionResult DuplicateAction(int id) { FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == id); // Create new name for duplicated action string newName = ""; if (action.Name.Contains(" ")) { // Check if the last part is a number string[] parts = action.Name.Split(' '); string lastPart = parts[parts.Length - 1]; int newNumber; if (int.TryParse(lastPart, out newNumber)) { // Increment the number newNumber++; // Reconstruct the name without the last part newName = string.Join(" ", parts.Take(parts.Length - 1)) + " " + newNumber; } else { // Add " 1" suffix newName = action.Name + " 1"; } } else { // If no space, just add " 1" newName = action.Name + " 1"; } // Check if the new name already exists for this floor bool nameExists = _unitOfWork.Action.Where(a => a.FloorId == action.FloorId && a.Name == newName).Any(); int counter = 1; // If the name exists, increment the counter until we find a unique name while (nameExists) { counter++; if (newName.Contains(" ")) { string[] parts = newName.Split(' '); int lastIndex = parts.Length - 1; if (int.TryParse(parts[lastIndex], out _)) { // Replace the last number parts[lastIndex] = counter.ToString(); newName = string.Join(" ", parts); } else { newName = newName + " " + counter; } } else { newName = newName + " " + counter; } nameExists = _unitOfWork.Action.Where(a => a.FloorId == action.FloorId && a.Name == newName).Any(); } // Create new action FloorAction newAction = new FloorAction() { Name = newName, FloorId = action.FloorId, A_Material_Cost = 0 }; _unitOfWork.Action.Add(newAction); _unitOfWork.save(); // Get all tasks of the original action IEnumerable tasks = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId); // Duplicate each task foreach (var task in tasks) { ActionTask newTask = new ActionTask() { Name = task.Name, ActionId = newAction.ActionId, Material_Cost = 0, Labor_Rate = task.Labor_Rate, Labor_Cost = 0, Measured_Work = 0, Mat_Lab = 0, Completion = 0, SDate = new DateOnly(2024, 5, 1), Duration = task.Duration, DisplayName = task.DisplayName }; _unitOfWork.Task.Add(newTask); _unitOfWork.save(); // Duplicate properties for this task IEnumerable properties = _unitOfWork.Property.Where(u => u.TaskId == task.TaskId); foreach (var property in properties) { Property newProperty = new Property() { Name = property.Name, TaskId = newTask.TaskId, Description = property.Description, Value = property.Value, Visible = property.Visible }; _unitOfWork.Property.Add(newProperty); _unitOfWork.save(); } // Duplicate materials for this task IEnumerable materials = _unitOfWork.Material.Where(u => u.TaskId == task.TaskId); foreach (var material in materials) { Material newMaterial = new Material() { Name = material.Name, ODCProject = material.ODCProject, ODCProjectId = material.ODCProjectId, TaskId = newTask.TaskId, ActionId = newAction.ActionId, FloorId = material.FloorId, Qty = 0, Unit = material.Unit, Unit_Cost = material.Unit_Cost }; _unitOfWork.Material.Add(newMaterial); _unitOfWork.save(); } } // Update calculations Refresh_Calculations(); TempData["success"] = "Action duplicated successfully!"; return RedirectToAction("FloorDetails", "ODCProject", new { id = action.FloorId }); } [Authorize(Roles = "Admin,Employee")] [HttpGet] public IActionResult EditActionM(int? id) { FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == id); return View(action); } [Authorize(Roles = "Admin,Employee")] [HttpPost] public IActionResult EditActionM(IFormCollection form) { // Debug what values are coming in foreach (var key in form.Keys) { var value = form[key]; System.Diagnostics.Debug.WriteLine($"Form field: {key} = {value}"); } // Manually recreate the action var actionId = int.Parse(form["ActionId"]); FloorAction action = _unitOfWork.Action.Get(u => u.ActionId == actionId); // Update only the fields from the form if (form.ContainsKey("DisplayNameA")) { action.DisplayNameA = form["DisplayNameA"]; } // Ensure Name has a value if (string.IsNullOrEmpty(action.Name)) { action.Name = action.DisplayNameA ?? "Default"; } _unitOfWork.Action.update(action); _unitOfWork.save(); TempData["success"] = "Action edited successfully"; return RedirectToAction("ActionDetails", "ODCProject", new { id = action.ActionId }); } // Add these methods at the end of ODCProjectController class, around line 1200+ [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public IActionResult TaskManagement(int id) // Project ID { Refresh_Calculations(); // Get project and tasks data var project = _unitOfWork.ODCProject.Get(u => u.Id == id); var floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == id).ToList(); var actions = new List(); var tasks = new List(); // Get all actions and tasks for the project foreach (var floor in floors) { var floorActions = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId).ToList(); actions.AddRange(floorActions); } foreach (var action in actions) { var actionTasks = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId).ToList(); tasks.AddRange(actionTasks); } // Prepare task details for display var taskDetails = new List(); foreach (var task in tasks) { var action = _unitOfWork.Action.Get(u => u.ActionId == task.ActionId); var floor = _unitOfWork.Floor.Get(u => u.FloorId == action.FloorId); taskDetails.Add(new TaskWith_Action_Floor { task = task, ActionName = action.EffectiveDisplayNameA, ActionId = action.ActionId, FloorName = floor.Name, FloorId = floor.FloorId }); } ViewBag.Project = project; ViewBag.ProjectId = id; ViewBag.TaskDetails = taskDetails; var viewModel = new TaskManagementViewModel { Tasks = tasks, ProjectId = id, NewSubtask = new Subtask { ProjectId = id, StartTimeStamp = DateTime.Now, EndTimeStamp = DateTime.Now.AddDays(1) } }; return View(viewModel); } // Get construction sheets for a specific task (for the dropdown) [HttpGet] public IActionResult GetTaskConstructionSheets(int taskId) { var constructionSheets = _unitOfWork.Subtask .Where(s => s.TaskId == taskId) .Select(s => new { subtaskId = s.SubtaskId, description = s.SubTaskD, startTimeStamp = s.StartTimeStamp }) .OrderByDescending(s => s.startTimeStamp) .ToList(); return Json(constructionSheets); } [Authorize(Roles = "Admin,Employee,Customer")] // Enhanced GetTaskMaterials method [HttpGet] public IActionResult GetTaskMaterials(int taskId, int? subtaskId = null) { var materials = _unitOfWork.Material .Where(m => m.TaskId == taskId) .Select(m => new { materialId = m.MaterialId, name = m.Name, plannedQty = m.Qty, actualQty = m.Actual_Qty, unit = m.Unit, unitCost = m.Unit_Cost, linkedToSubtask = m.SubtaskId, isLinkedToCurrent = m.SubtaskId == subtaskId }).ToList(); return Json(materials); } [Authorize(Roles = "Admin,Employee,Customer")] // Enhanced TaskManagement POST method [HttpPost] public IActionResult TaskManagement(TaskManagementViewModel model, string submitType, List Materials) { try { if (submitType == "completion") { // Handle completion percentage updates (existing code) if (model.Tasks != null && model.Tasks.Any()) { foreach (var task in model.Tasks) { var existingTask = _unitOfWork.Task.Get(u => u.TaskId == task.TaskId); if (existingTask != null) { existingTask.Completion = task.Completion; existingTask.Labor_Cost = existingTask.Measured_Work * existingTask.Labor_Rate; existingTask.Mat_Lab = existingTask.Labor_Cost + existingTask.Material_Cost; _unitOfWork.Task.update(existingTask); } } _unitOfWork.save(); TempData["success"] = "Completion percentages updated successfully"; } } else if (submitType == "construction") { // Handle construction sheet creation (existing code) if (ModelState.IsValid) { if (model.NewSubtask.EndTimeStamp > model.NewSubtask.StartTimeStamp) { if (model.NewSubtask.FileUpload != null) { using (var memoryStream = new MemoryStream()) { model.NewSubtask.FileUpload.CopyTo(memoryStream); model.NewSubtask.FileData = memoryStream.ToArray(); model.NewSubtask.FileContentType = model.NewSubtask.FileUpload.ContentType; model.NewSubtask.FileName = model.NewSubtask.FileUpload.FileName; } } model.NewSubtask.ProjectId = model.ProjectId; _unitOfWork.Subtask.Add(model.NewSubtask); _unitOfWork.save(); TempData["success"] = "Construction sheet created successfully"; } else { TempData["error"] = "End time must be after start time"; } } } else if (submitType == "materials") { // Handle material consumption updates (NEW) if (Materials != null && Materials.Any()) { var subtaskId = model.MaterialConsumption.SelectedSubtaskId; var recordedBy = User.Identity?.Name; var recordedDate = DateTime.Now; foreach (var materialItem in Materials) { if (materialItem.NewActualQty.HasValue) { var material = _unitOfWork.Material.Get(m => m.MaterialId == materialItem.MaterialId); if (material != null) { material.Actual_Qty = materialItem.NewActualQty; material.SubtaskId = subtaskId; material.ActualQtyRecordedDate = recordedDate; material.RecordedBy = recordedBy; _unitOfWork.Material.update(material); } } } _unitOfWork.save(); TempData["success"] = $"Material consumption recorded for {Materials.Count(m => m.NewActualQty.HasValue)} materials"; } else { TempData["error"] = "No material quantities provided"; } } } catch (Exception ex) { TempData["error"] = "An error occurred: " + ex.Message; System.Diagnostics.Debug.WriteLine($"TaskManagement POST Error: {ex.Message}"); } return RedirectToAction("TaskManagement", new { id = model.ProjectId }); } [Authorize(Roles = "Admin,Employee,Customer")] // Material Report Generation [HttpGet] public IActionResult GenerateMaterialReport(int projectId) { try { var project = _unitOfWork.ODCProject.Get(u => u.Id == projectId); var materials = _unitOfWork.Material .Where(m => m.ODCProjectId == projectId) .ToList(); using (ExcelEngine excelEngine = new ExcelEngine()) { IApplication application = excelEngine.Excel; application.DefaultVersion = ExcelVersion.Excel2016; IWorkbook workbook = application.Workbooks.Create(1); IWorksheet worksheet = workbook.Worksheets[0]; // Set title worksheet.Range["A1"].Text = $"Material Variance Report - {project.Name}"; worksheet.Range["A1:J1"].Merge(); worksheet.Range["A1"].CellStyle.Font.Bold = true; worksheet.Range["A1"].CellStyle.Font.Size = 16; worksheet.Range["A1"].CellStyle.HorizontalAlignment = ExcelHAlign.HAlignCenter; //worksheet.Range["A1"].CellStyle.Color = Color.FromArgb(0, 120, 215); //worksheet.Range["A1"].CellStyle.Font.Color = ExcelKnownColors.White; // Add project info and generation date worksheet.Range["A2"].Text = $"Generated on: {DateTime.Now:MMMM dd, yyyy 'at' HH:mm}"; worksheet.Range["A2:J2"].Merge(); worksheet.Range["A2"].CellStyle.Font.Size = 10; worksheet.Range["A2"].CellStyle.HorizontalAlignment = ExcelHAlign.HAlignCenter; worksheet.Range["A2"].CellStyle.Font.Italic = true; // Headers row string[] headers = { "Material Name", "Unit", "Planned Qty", "Actual Qty", "Quantity Variance", "Unit Cost", "Planned Cost", "Actual Cost", "Cost Variance", "Variance %" }; int headerRow = 4; // Start headers at row 4 for (int i = 0; i < headers.Length; i++) { worksheet.Range[headerRow, i + 1].Text = headers[i]; worksheet.Range[headerRow, i + 1].CellStyle.Font.Bold = true; worksheet.Range[headerRow, i + 1].CellStyle.Font.Size = 11; //worksheet.Range[headerRow, i + 1].CellStyle.Color = Color.FromArgb(68, 114, 196); // Blue header worksheet.Range[headerRow, i + 1].CellStyle.Font.Color = ExcelKnownColors.White; worksheet.Range[headerRow, i + 1].CellStyle.HorizontalAlignment = ExcelHAlign.HAlignCenter; worksheet.Range[headerRow, i + 1].CellStyle.VerticalAlignment = ExcelVAlign.VAlignCenter; } // Add borders to headers worksheet.Range[headerRow, 1, headerRow, headers.Length].BorderAround(ExcelLineStyle.Thick); worksheet.Range[headerRow, 1, headerRow, headers.Length].BorderInside(ExcelLineStyle.Thin); // Data rows int dataStartRow = headerRow + 1; int currentRow = dataStartRow; double totalPlannedCost = 0; double totalActualCost = 0; int materialsWithData = 0; var materialsWithActual = materials.Where(m => m.Actual_Qty.HasValue).ToList(); if (materialsWithActual.Any()) { foreach (var material in materialsWithActual) { var qtyVariance = material.QuantityVariance ?? 0; var plannedCost = material.Qty * (material.Unit_Cost ?? 0); var actualCost = material.ActualCost ?? 0; var costVariance = material.CostVariance ?? 0; var variancePercent = plannedCost != 0 ? (costVariance / plannedCost) : 0; // Material data worksheet.Range[currentRow, 1].Text = material.Name; worksheet.Range[currentRow, 2].Text = material.Unit ?? "N/A"; worksheet.Range[currentRow, 3].Number = material.Qty; worksheet.Range[currentRow, 4].Number = material.Actual_Qty ?? 0; worksheet.Range[currentRow, 5].Number = qtyVariance; worksheet.Range[currentRow, 6].Number = material.Unit_Cost ?? 0; worksheet.Range[currentRow, 7].Number = plannedCost; worksheet.Range[currentRow, 8].Number = actualCost; worksheet.Range[currentRow, 9].Number = costVariance; worksheet.Range[currentRow, 10].Number = variancePercent; // Color coding for variances if (Math.Abs(variancePercent) > 10) // Significant variance (>10%) { if (costVariance > 0) // Over budget { // worksheet.Range[currentRow, 9].CellStyle.Color = Color.FromArgb(255, 199, 206); // Light red //worksheet.Range[currentRow, 10].CellStyle.Color = Color.FromArgb(255, 199, 206); } else if (costVariance < 0) // Under budget { //worksheet.Range[currentRow, 9].CellStyle.Color = Color.FromArgb(198, 239, 206); // Light green //worksheet.Range[currentRow, 10].CellStyle.Color = Color.FromArgb(198, 239, 206); } } // Alternate row colors for better readability if (currentRow % 2 == 0) { //worksheet.Range[currentRow, 1, currentRow, headers.Length].CellStyle.Color = Color.FromArgb(242, 242, 242); } totalPlannedCost += plannedCost; totalActualCost += actualCost; materialsWithData++; currentRow++; } } else { // No data message worksheet.Range[currentRow, 1].Text = "No material consumption data recorded yet"; worksheet.Range[currentRow, 1, currentRow, headers.Length].Merge(); worksheet.Range[currentRow, 1].CellStyle.HorizontalAlignment = ExcelHAlign.HAlignCenter; worksheet.Range[currentRow, 1].CellStyle.Font.Italic = true; //worksheet.Range[currentRow, 1].CellStyle.Color = Color.FromArgb(255, 255, 204); // Light yellow currentRow++; } // Totals section (only if there's data) if (materialsWithData > 0) { currentRow++; // Empty row before totals // Totals header worksheet.Range[currentRow, 1].Text = "PROJECT TOTALS"; worksheet.Range[currentRow, 1, currentRow, 6].Merge(); worksheet.Range[currentRow, 1].CellStyle.Font.Bold = true; worksheet.Range[currentRow, 1].CellStyle.Font.Size = 12; //worksheet.Range[currentRow, 1].CellStyle.Color = Color.FromArgb(0, 32, 96); // Dark blue worksheet.Range[currentRow, 1].CellStyle.Font.Color = ExcelKnownColors.White; worksheet.Range[currentRow, 1].CellStyle.HorizontalAlignment = ExcelHAlign.HAlignCenter; // Total values worksheet.Range[currentRow, 7].Number = totalPlannedCost; worksheet.Range[currentRow, 8].Number = totalActualCost; worksheet.Range[currentRow, 9].Number = totalActualCost - totalPlannedCost; worksheet.Range[currentRow, 10].Formula = totalPlannedCost != 0 ? $"=({totalActualCost - totalPlannedCost}/{totalPlannedCost})" : "0"; // Format totals row worksheet.Range[currentRow, 7, currentRow, 10].CellStyle.Font.Bold = true; worksheet.Range[currentRow, 7, currentRow, 10].CellStyle.Font.Size = 11; // worksheet.Range[currentRow, 1, currentRow, headers.Length].CellStyle.Color = Color.FromArgb(0, 32, 96); worksheet.Range[currentRow, 7, currentRow, 10].CellStyle.Font.Color = ExcelKnownColors.White; // Add summary statistics currentRow += 2; worksheet.Range[currentRow, 1].Text = "SUMMARY STATISTICS"; worksheet.Range[currentRow, 1].CellStyle.Font.Bold = true; worksheet.Range[currentRow, 1].CellStyle.Font.Size = 12; currentRow++; worksheet.Range[currentRow, 1].Text = $"Total Materials Tracked: {materialsWithData}"; currentRow++; worksheet.Range[currentRow, 1].Text = $"Total Materials in Project: {materials.Count}"; currentRow++; worksheet.Range[currentRow, 1].Text = $"Tracking Completion: {(materialsWithData * 100.0 / materials.Count):F1}%"; } // Format currency and percentage columns int lastDataRow = materialsWithData > 0 ? dataStartRow + materialsWithData : dataStartRow; worksheet.Range[dataStartRow, 6, lastDataRow, 9].NumberFormat = "$#,##0.00"; worksheet.Range[dataStartRow, 10, lastDataRow, 10].NumberFormat = "0.00%"; // Add borders to data area if (materialsWithData > 0) { worksheet.Range[dataStartRow, 1, lastDataRow, headers.Length].BorderAround(ExcelLineStyle.Thin); //worksheet.Range[dataStartRow, 1, lastDataRow, headers.Length].BorderInside(ExcelLineStyle.Thin, ExcelLineStyle.Thin); } // Auto-fit columns worksheet.UsedRange.AutofitColumns(); // Set column widths for better appearance worksheet.Range["A:A"].ColumnWidth = 20; // Material Name worksheet.Range["B:B"].ColumnWidth = 10; // Unit worksheet.Range["C:E"].ColumnWidth = 12; // Quantities worksheet.Range["F:I"].ColumnWidth = 15; // Costs worksheet.Range["J:J"].ColumnWidth = 12; // Percentage // Save and return using (var stream = new MemoryStream()) { workbook.SaveAs(stream); stream.Position = 0; string fileName = $"MaterialVarianceReport_{project.Name.Replace(" ", "_")}_{DateTime.Now:yyyyMMdd_HHmm}.xlsx"; return File( stream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName ); } } } catch (Exception ex) { TempData["error"] = "Error generating material report: " + ex.Message; return RedirectToAction("TaskManagement", new { id = projectId }); } } // Alternative method if you prefer separate POST actions [Authorize(Roles = "Admin,Employee,Customer")] [HttpPost] public IActionResult UpdateTaskCompletion(List tasks, int projectId) { try { if (tasks != null && tasks.Any()) { foreach (var task in tasks) { var existingTask = _unitOfWork.Task.Get(u => u.TaskId == task.TaskId); if (existingTask != null) { existingTask.Completion = task.Completion; existingTask.Labor_Cost = existingTask.Measured_Work * existingTask.Labor_Rate; existingTask.Mat_Lab = existingTask.Labor_Cost + existingTask.Material_Cost; _unitOfWork.Task.update(existingTask); } } _unitOfWork.save(); TempData["success"] = "Completion percentages updated successfully"; } else { TempData["error"] = "No tasks to update"; } } catch (Exception ex) { TempData["error"] = "Error updating completions: " + ex.Message; } return RedirectToAction("TaskManagement", new { id = projectId }); } [Authorize(Roles = "Admin,Employee,Customer")] [HttpGet] public async Task GenerateProjectMaterialReport(int id) { try { ODCProject project = _unitOfWork.ODCProject.Get(u => u.Id == id); if (project == null) { TempData["error"] = "Project not found"; return RedirectToAction("Index", "ODCProject"); } using (ExcelEngine engine = new ExcelEngine()) { IApplication application = engine.Excel; application.DefaultVersion = ExcelVersion.Xlsx; IWorkbook workbook = application.Workbooks.Create(1); IWorksheet worksheet = workbook.Worksheets[0]; // Enable calculations workbook.CalculationOptions.CalculationMode = ExcelCalculationMode.Automatic; application.EnableIncrementalFormula = true; // Set up the worksheet worksheet.IsGridLinesVisible = true; worksheet.Range[1, 1, 1000, 20].WrapText = true; // Title Section worksheet.Range["A1:J1"].Merge(); worksheet.Range["A1"].Text = $"MATERIAL REPORT - {project.Name.ToUpper()}"; worksheet.Range["A1"].CellStyle.Font.Bold = true; worksheet.Range["A1"].CellStyle.Font.Size = 18; worksheet.Range["A1"].CellStyle.HorizontalAlignment = ExcelHAlign.HAlignCenter; worksheet.Range["A1"].CellStyle.Color = Syncfusion.Drawing.Color.Navy; worksheet.Range["A1"].CellStyle.Font.Color = ExcelKnownColors.White; worksheet.Range["A1"].RowHeight = 35; // Project Info Section worksheet.Range["A2:J2"].Merge(); worksheet.Range["A2"].Text = $"Generated on: {DateTime.Now:MMMM dd, yyyy 'at' HH:mm tt}"; worksheet.Range["A2"].CellStyle.Font.Size = 11; worksheet.Range["A2"].CellStyle.HorizontalAlignment = ExcelHAlign.HAlignCenter; worksheet.Range["A2"].CellStyle.Font.Italic = true; worksheet.Range["A2"].CellStyle.Color = Syncfusion.Drawing.Color.LightBlue; int currentRow = 4; // Summary Section Headers string[] summaryHeaders = { "Floor", "Action", "Task", "Material Name", "Unit", "Planned Qty", "Actual Qty", "Unit Cost", "Planned Cost", "Actual Cost", "Variance" }; for (int i = 0; i < summaryHeaders.Length; i++) { worksheet.Range[currentRow, i + 1].Text = summaryHeaders[i]; worksheet.Range[currentRow, i + 1].CellStyle.Font.Bold = true; worksheet.Range[currentRow, i + 1].CellStyle.Font.Size = 12; worksheet.Range[currentRow, i + 1].CellStyle.Color = Syncfusion.Drawing.Color.DarkSlateBlue; worksheet.Range[currentRow, i + 1].CellStyle.Font.Color = ExcelKnownColors.White; worksheet.Range[currentRow, i + 1].CellStyle.HorizontalAlignment = ExcelHAlign.HAlignCenter; } // Set column widths worksheet.Range["A:A"].ColumnWidth = 15; // Floor worksheet.Range["B:B"].ColumnWidth = 20; // Action worksheet.Range["C:C"].ColumnWidth = 25; // Task worksheet.Range["D:D"].ColumnWidth = 30; // Material Name worksheet.Range["E:E"].ColumnWidth = 10; // Unit worksheet.Range["F:H"].ColumnWidth = 12; // Quantities and Unit Cost worksheet.Range["I:K"].ColumnWidth = 15; // Costs and Variance currentRow++; int dataStartRow = currentRow; // Totals tracking double totalPlannedCost = 0; double totalActualCost = 0; int materialCount = 0; // Get all floors for the project IEnumerable floors = _unitOfWork.Floor.Where(u => u.ODCProjectId == id); foreach (var floor in floors) { // Get all actions for this floor IEnumerable actions = _unitOfWork.Action.Where(u => u.FloorId == floor.FloorId); foreach (var action in actions) { // Get all tasks for this action IEnumerable tasks = _unitOfWork.Task.Where(u => u.ActionId == action.ActionId); foreach (var task in tasks) { // Get all materials for this task IEnumerable materials = _unitOfWork.Material.Where(u => u.TaskId == task.TaskId); foreach (var material in materials) { // Floor name worksheet.Range[currentRow, 1].Text = floor.Name; // Action name (use DisplayName if available, otherwise Name) worksheet.Range[currentRow, 2].Text = !string.IsNullOrEmpty(action.DisplayNameA) ? action.DisplayNameA : action.Name; // Task name worksheet.Range[currentRow, 3].Text = task.Name; // Material details worksheet.Range[currentRow, 4].Text = material.Name; worksheet.Range[currentRow, 5].Text = material.Unit ?? "N/A"; worksheet.Range[currentRow, 6].Number = material.Qty; worksheet.Range[currentRow, 7].Number = material.Actual_Qty ?? 0; worksheet.Range[currentRow, 8].Number = material.Unit_Cost ?? 0; // Calculate costs double plannedCost = material.Qty * (material.Unit_Cost ?? 0); double actualCost = (material.Actual_Qty ?? 0) * (material.Unit_Cost ?? 0); double variance = actualCost - plannedCost; worksheet.Range[currentRow, 9].Number = plannedCost; worksheet.Range[currentRow, 10].Number = actualCost; worksheet.Range[currentRow, 11].Number = variance; // Color coding for variance if (variance > 0) // Over budget { worksheet.Range[currentRow, 11].CellStyle.Color = Syncfusion.Drawing.Color.LightPink; } else if (variance < 0) // Under budget { worksheet.Range[currentRow, 11].CellStyle.Color = Syncfusion.Drawing.Color.LightGreen; } // Alternate row coloring for better readability if (materialCount % 2 == 0) { for (int col = 1; col <= 11; col++) { if (col != 11) // Don't override variance coloring worksheet.Range[currentRow, col].CellStyle.Color = Syncfusion.Drawing.Color.AliceBlue; } } // Update totals totalPlannedCost += plannedCost; totalActualCost += actualCost; materialCount++; currentRow++; } } } } // Add borders to data section if (materialCount > 0) { worksheet.Range[dataStartRow - 1, 1, currentRow - 1, 11].BorderAround(ExcelLineStyle.Thick); worksheet.Range[dataStartRow - 1, 1, currentRow - 1, 11].BorderInside(ExcelLineStyle.Thin); } // Totals Section currentRow += 2; // Total headers worksheet.Range[currentRow, 8].Text = "TOTALS"; worksheet.Range[currentRow, 8].CellStyle.Font.Bold = true; worksheet.Range[currentRow, 8].CellStyle.Font.Size = 14; worksheet.Range[currentRow, 8].CellStyle.Color = Syncfusion.Drawing.Color.Gold; worksheet.Range[currentRow, 9].Number = totalPlannedCost; worksheet.Range[currentRow, 10].Number = totalActualCost; worksheet.Range[currentRow, 11].Number = totalActualCost - totalPlannedCost; // Format totals worksheet.Range[currentRow, 9, currentRow, 11].CellStyle.Font.Bold = true; worksheet.Range[currentRow, 9, currentRow, 11].CellStyle.Font.Size = 14; worksheet.Range[currentRow, 9, currentRow, 11].CellStyle.Color = Syncfusion.Drawing.Color.Gold; worksheet.Range[currentRow, 8, currentRow, 11].BorderAround(ExcelLineStyle.Thick); // Summary Statistics currentRow += 3; worksheet.Range[currentRow, 1].Text = "SUMMARY STATISTICS"; worksheet.Range[currentRow, 1].CellStyle.Font.Bold = true; worksheet.Range[currentRow, 1].CellStyle.Font.Size = 14; worksheet.Range[currentRow, 1].CellStyle.Color = Syncfusion.Drawing.Color.LightGray; currentRow++; worksheet.Range[currentRow, 1].Text = $"Total Materials: {materialCount}"; currentRow++; worksheet.Range[currentRow, 1].Text = $"Total Planned Cost: ${totalPlannedCost:N2}"; currentRow++; worksheet.Range[currentRow, 1].Text = $"Total Actual Cost: ${totalActualCost:N2}"; currentRow++; worksheet.Range[currentRow, 1].Text = $"Total Variance: ${(totalActualCost - totalPlannedCost):N2}"; currentRow++; if (totalPlannedCost > 0) { double variancePercent = ((totalActualCost - totalPlannedCost) / totalPlannedCost) * 100; worksheet.Range[currentRow, 1].Text = $"Variance Percentage: {variancePercent:F2}%"; } // Format currency columns worksheet.Range[dataStartRow, 8, currentRow, 11].NumberFormat = "$#,##0.00"; // Materials with no actual quantities section var materialsWithoutActual = _unitOfWork.Material .Where(m => m.ODCProjectId == id && (!m.Actual_Qty.HasValue || m.Actual_Qty == 0)) .ToList(); if (materialsWithoutActual.Any()) { currentRow += 3; worksheet.Range[currentRow, 1].Text = "MATERIALS WITHOUT ACTUAL QUANTITIES"; worksheet.Range[currentRow, 1, currentRow, 6].Merge(); worksheet.Range[currentRow, 1].CellStyle.Font.Bold = true; worksheet.Range[currentRow, 1].CellStyle.Font.Size = 14; worksheet.Range[currentRow, 1].CellStyle.Color = Syncfusion.Drawing.Color.Orange; worksheet.Range[currentRow, 1].CellStyle.Font.Color = ExcelKnownColors.White; currentRow++; string[] pendingHeaders = { "Floor", "Action", "Task", "Material Name", "Planned Qty", "Unit" }; for (int i = 0; i < pendingHeaders.Length; i++) { worksheet.Range[currentRow, i + 1].Text = pendingHeaders[i]; worksheet.Range[currentRow, i + 1].CellStyle.Font.Bold = true; worksheet.Range[currentRow, i + 1].CellStyle.Color = Syncfusion.Drawing.Color.LightGray; } currentRow++; foreach (var material in materialsWithoutActual) { var task = _unitOfWork.Task.Get(t => t.TaskId == material.TaskId); var action = _unitOfWork.Action.Get(a => a.ActionId == task.ActionId); var floor = _unitOfWork.Floor.Get(f => f.FloorId == action.FloorId); worksheet.Range[currentRow, 1].Text = floor.Name; worksheet.Range[currentRow, 2].Text = !string.IsNullOrEmpty(action.DisplayNameA) ? action.DisplayNameA : action.Name; worksheet.Range[currentRow, 3].Text = task.Name; worksheet.Range[currentRow, 4].Text = material.Name; worksheet.Range[currentRow, 5].Number = material.Qty; worksheet.Range[currentRow, 6].Text = material.Unit ?? "N/A"; currentRow++; } worksheet.Range[currentRow - materialsWithoutActual.Count - 1, 1, currentRow - 1, 6].BorderAround(ExcelLineStyle.Thin); } // Auto-fit remaining columns worksheet.UsedRange.AutofitColumns(); // Save the file MemoryStream memoryStream = new MemoryStream(); workbook.SaveAs(memoryStream); memoryStream.Position = 0; string fileName = $"MaterialReport_{project.Name.Replace(" ", "_")}_{DateTime.Now:yyyyMMdd_HHmm}.xlsx"; FileStreamResult fileStreamResult = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); fileStreamResult.FileDownloadName = fileName; return fileStreamResult; } } catch (Exception ex) { TempData["error"] = "Error generating material report: " + ex.Message; System.Diagnostics.Debug.WriteLine($"Material Report Error: {ex.Message}"); return RedirectToAction("Index", "ODCProject"); } } } public class ConstructionSheetDetail { public Subtask Subtask { get; set; } public string TaskName { get; set; } public double? TaskCompletion { get; set; } public string ActionName { get; set; } public string FloorName { get; set; } public double Duration { get; set; } } public class ProjectSummaryData { public ODCProject Project { get; set; } public int TotalFloors { get; set; } public int TotalActions { get; set; } public int TotalTasks { get; set; } public double TotalBudget { get; set; } public int CompletedTasks { get; set; } public int InProgressTasks { get; set; } public int NotStartedTasks { get; set; } public double AverageCompletion { get; set; } public double TotalMaterialCost { get; set; } public double TotalLaborCost { get; set; } public double TotalProjectCost { get; set; } public double TotalPlannedBudget { get; set; } public double TotalActualCost { get; set; } public double BudgetVariance { get; set; } public int TotalConstructionSheets { get; set; } public int TasksWithDocumentation { get; set; } public double DocumentationRate { get; set; } public DateOnly? EarliestStart { get; set; } public DateOnly? LatestEnd { get; set; } public int TotalDuration { get; set; } public List FloorSummaries { get; set; } public List RecentActivities { get; set; } } public class FloorSummary { public string FloorName { get; set; } public int TotalTasks { get; set; } public int CompletedTasks { get; set; } public double AverageCompletion { get; set; } public double MaterialCost { get; set; } public double LaborCost { get; set; } public double TotalCost { get; set; } } public class RecentActivity { public DateTime Date { get; set; } public string Description { get; set; } public string TaskName { get; set; } } public class ProgressDocumentationItem { public string FloorName { get; set; } public string ActionName { get; set; } public string TaskName { get; set; } public double TaskCompletion { get; set; } public int TaskId { get; set; } public List ConstructionSheets { get; set; } public bool HasDocumentation { get; set; } public int TotalSheets { get; set; } public DateTime? LatestUpdate { get; set; } } public class CostAnalysisData { public ODCProject Project { get; set; } public double TotalMaterialCost { get; set; } public double TotalLaborCost { get; set; } public double TotalProjectCost { get; set; } public double TotalPlannedBudget { get; set; } public double TotalActualCost { get; set; } public double BudgetVariance { get; set; } public double BudgetVariancePercentage { get; set; } public double MaterialPercentage { get; set; } public double LaborPercentage { get; set; } public List MaterialCostBreakdown { get; set; } public List FloorCostBreakdown { get; set; } public List TopExpensiveTasks { get; set; } public int TotalTasks { get; set; } public int TotalMaterials { get; set; } public double AverageTaskCost { get; set; } } public class MaterialCostItem { public string MaterialName { get; set; } public double PlannedQuantity { get; set; } public double ActualQuantity { get; set; } public double UnitCost { get; set; } public double PlannedCost { get; set; } public double ActualCost { get; set; } public string Unit { get; set; } } public class FloorCostAnalysis { public string FloorName { get; set; } public double MaterialCost { get; set; } public double LaborCost { get; set; } public double TotalCost { get; set; } public double PercentageOfTotal { get; set; } } public class TaskCostItem { public string TaskName { get; set; } public string FloorName { get; set; } public string ActionName { get; set; } public double MaterialCost { get; set; } public double LaborCost { get; set; } public double TotalCost { get; set; } public double Completion { get; set; } } public class ScheduleReportData { public ODCProject Project { get; set; } public DateOnly? ProjectStartDate { get; set; } public DateOnly? ProjectEndDate { get; set; } public int TotalProjectDuration { get; set; } public int TotalTasks { get; set; } public int CompletedTasks { get; set; } public int InProgressTasks { get; set; } public int NotStartedTasks { get; set; } public int OverdueTasks { get; set; } public int OnScheduleTasks { get; set; } public double ScheduleComplianceRate { get; set; } public List TaskScheduleDetails { get; set; } public List FloorScheduleSummary { get; set; } public List CriticalPathTasks { get; set; } } public class TaskScheduleItem { public string TaskName { get; set; } public string FloorName { get; set; } public string ActionName { get; set; } public DateOnly StartDate { get; set; } public DateOnly? EndDate { get; set; } public int Duration { get; set; } public double Completion { get; set; } public int DaysElapsed { get; set; } public int DaysRemaining { get; set; } public string Status { get; set; } } public class FloorScheduleSummary { public string FloorName { get; set; } public int TotalTasks { get; set; } public DateOnly? StartDate { get; set; } public DateOnly? EndDate { get; set; } public int Duration { get; set; } public int CompletedTasks { get; set; } public int InProgressTasks { get; set; } public int NotStartedTasks { get; set; } } }