/// <summary> /// Release properties from project, such as during the deny or cancelled statuses /// </summary> /// <param name="context"></param> /// <param name="project"></param> /// <returns></returns> public static void ReleaseProjectProperties(this PimsContext context, Entity.Project project) { project.Properties.ForEach(p => { context.Update(p.UpdateProjectNumber(null)); }); }
/// <summary> /// Dispose properties from project, during the disposed workflow status. /// </summary> /// <param name="context"></param> /// <param name="project"></param> /// <returns></returns> public static void DisposeProjectProperties(this PimsContext context, Entity.Project project) { var disposed = context.PropertyClassifications.AsNoTracking().FirstOrDefault(c => c.Name == "Disposed") ?? throw new KeyNotFoundException("Classification 'Disposed' not found."); var parentParcels = GetSubdivisionParentParcels(project); DisposeSubdivisionParentParcels(context, parentParcels); project.Properties.Where(p => !parentParcels.Any(pp => pp.Id == p.Id)).ForEach(p => { switch (p.PropertyType) { case (Entity.PropertyTypes.Land): if (p.Parcel == null) { throw new InvalidOperationException("Unable to update parcel status."); } p.Parcel.ClassificationId = disposed.Id; p.Parcel.AgencyId = null; p.Parcel.PropertyTypeId = (int)PropertyTypes.Land; // all subdivisions should be transitioned to parcels after they are disposed. p.Parcel.Parcels.Clear(); // remove all references to parent parcels. break; case (Entity.PropertyTypes.Building): if (p.Building == null) { throw new InvalidOperationException("Unable to update building status."); } p.Building.ClassificationId = disposed.Id; p.Building.AgencyId = null; break; } context.Update(p); }); }
/// Get a collection of responses that have changed from the original to the updated project. /// </summary> /// <param name="originalProject"></param> /// <param name="updatedProject"></param> /// <returns></returns> public static IEnumerable <ProjectAgencyResponse> GetResponseChanges(this Entity.Project originalProject, Entity.Project updatedProject) { if (updatedProject == null) { throw new ArgumentNullException(nameof(updatedProject)); } var responses = new List <ProjectAgencyResponse>(updatedProject.Responses.Count()); foreach (var response in updatedProject.Responses) { var originalResponse = originalProject.Responses.FirstOrDefault(r => r.ProjectId == response.ProjectId && r.AgencyId == response.AgencyId); if (originalResponse == null || originalResponse.Response != response.Response) { responses.Add(response); } } // Any responses that were deleted should now be ignored. foreach (var response in originalProject.Responses) { // Look for original response in the update project. var updatedResponse = updatedProject.Responses.FirstOrDefault(r => r.ProjectId == response.ProjectId && r.AgencyId == response.AgencyId); if (updatedResponse == null) { response.Response = NotificationResponses.Unsubscribe; responses.Add(response); } } return(responses); }
/// <summary> /// Dispose properties from project, during the disposed workflow status. /// </summary> /// <param name="context"></param> /// <param name="project"></param> /// <returns></returns> public static void DisposeProjectProperties(this PimsContext context, Entity.Project project) { var disposed = context.PropertyClassifications.Find(4) ?? throw new KeyNotFoundException("Classification 'Disposed' not found."); project.Properties.ForEach(p => { switch (p.PropertyType) { case (Entity.PropertyTypes.Land): if (p.Parcel == null) { throw new InvalidOperationException("Unable to update parcel status."); } p.Parcel.ClassificationId = disposed.Id; p.Parcel.AgencyId = null; break; case (Entity.PropertyTypes.Building): if (p.Building == null) { throw new InvalidOperationException("Unable to update building status."); } p.Building.ClassificationId = disposed.Id; p.Building.AgencyId = null; break; } context.Update(p); }); }
/// <summary> /// Find the property for the specified 'pid' in inventory and add it to the project. /// Assumption is made that if the parcel is added, then also add all the buildings on the parcel. /// </summary> /// <param name="project"></param> /// <param name="pid"></param> /// <returns></returns> private Entity.Project AddProperty(Entity.Project project, string pid) { try { var pidValue = Int32.Parse(pid.Replace("-", "")); var parcel = _adminService.Parcel.GetByPid(pidValue); project.AddProperty(parcel).ForEach(p => { p.Project = null; p.Parcel = null; // Need to do this so that it isn't reattached. }); var buildings = parcel.Buildings.Select(b => b.Building).ToArray(); project.AddProperty(buildings).ForEach(b => { b.Project = null; b.Building = null; // Need to do this so that it isn't reattached. }); _logger.LogInformation($"Property '{pid}' added to project '{project.ProjectNumber}'."); } catch (KeyNotFoundException) { _logger.LogWarning($"Property '{pid}' not found on project '{project.ProjectNumber}'."); } catch (InvalidOperationException ex) { _logger.LogWarning(ex, $"Property '{pid}' not found on project '{project.ProjectNumber}'."); } return(project); }
public void ExportProjects_Csv_Query_Success() { // Arrange var helper = new TestHelper(); var controller = helper.CreateController <ProjectController>(Permissions.PropertyView); var headers = helper.GetService <Mock <Microsoft.AspNetCore.Http.IHeaderDictionary> >(); headers.Setup(m => m["Accept"]).Returns(ContentTypes.CONTENT_TYPE_CSV); var agency = EntityHelper.CreateAgency(1); var project = new Entity.Project() { Agency = agency }; var projects = new[] { project }; var service = helper.GetService <Mock <IPimsService> >(); var mapper = helper.GetService <IMapper>(); var page = new Paged <Entity.Project>(projects); service.Setup(m => m.Project.GetPage(It.IsAny <Entity.Models.ProjectFilter>())).Returns(page); // Act var result = controller.ExportProjects(); // Assert var actionResult = Assert.IsType <ContentResult>(result); var actualResult = Assert.IsType <string>(actionResult.Content); Assert.Equal(ContentTypes.CONTENT_TYPE_CSV, actionResult.ContentType); service.Verify(m => m.Project.GetPage(It.IsAny <Entity.Models.ProjectFilter>()), Times.Once()); }
public void ExportProjects_ExcelX_Query_Success(Uri uri) { // Arrange var helper = new TestHelper(); var controller = helper.CreateController <ProjectController>(Permissions.PropertyView, uri); var headers = helper.GetService <Mock <Microsoft.AspNetCore.Http.IHeaderDictionary> >(); headers.Setup(m => m["Accept"]).Returns(ContentTypes.CONTENT_TYPE_EXCELX); var agency = EntityHelper.CreateAgency(1); var project = new Entity.Project() { Agency = agency }; var projects = new[] { project }; var service = helper.GetService <Mock <IPimsService> >(); var mapper = helper.GetService <IMapper>(); var page = new Paged <Entity.Project>(projects); service.Setup(m => m.Project.GetPage(It.IsAny <Entity.Models.ProjectFilter>())).Returns(page); // Act var result = controller.ExportProjects(); // Assert var actionResult = Assert.IsType <FileStreamResult>(result); Assert.Equal(ContentTypes.CONTENT_TYPE_EXCELX, actionResult.ContentType); Assert.NotNull(actionResult.FileDownloadName); Assert.True(actionResult.FileStream.Length > 0); service.Verify(m => m.Project.GetPage(It.IsAny <Entity.Models.ProjectFilter>()), Times.Once()); }
/// <summary> /// A parcel can only be updated or removed if not within an active project or user has admin-properties permission /// </summary> /// <param name="service"></param> /// <param name="project"></param> /// <param name="options"></param> /// <returns></returns> public static void ThrowIfNotAllowedToUpdate(this BaseService service, Entity.Project project, ProjectOptions options) { if (project != null && !project.IsProjectEditable(service.GetUser(), options)) { throw new NotAuthorizedException("Cannot update or delete an active project."); } }
/// <summary> /// Determine if the project is in draft. /// </summary> /// <param name="project"></param> /// <param name="options"></param> /// <returns></returns> public static bool IsProjectInDraft(this Entity.Project project, ProjectOptions options) { if (project.Workflow == null) { throw new ArgumentNullException(nameof(project), "The 'Workflow' cannot be null."); } return(options.DraftWorkflows.Contains(project.Workflow.Code)); }
/// <summary> /// Get the latest project associated to this property, using the workflow sort order. /// </summary> /// <param name="property"></param> /// <returns>The workflow code of the latest workflow associated to this property</returns> public static Entity.Project GetLatestProject(this Entity.Property property) { Entity.Project latestProject = null; if (property is Entity.Parcel parcel && parcel.Projects.Select(pp => pp.Project).Any()) { latestProject = parcel.Projects.Select(pp => pp.Project). Aggregate((Entity.Project projectWithLatestWorkflow, Entity.Project current) => current.Workflow?.SortOrder > projectWithLatestWorkflow?.Workflow?.SortOrder && current?.Status?.IsTerminal == false ? current : projectWithLatestWorkflow); }
/// <summary> /// Determine the financial year the project is based on. /// Scans properties and gets the most recent evaluation date. /// </summary> /// <param name="project"></param> /// <returns></returns> public static DateTime GetProjectFinancialDate(this Entity.Project project) // TODO: This is most likely invalid, but for now will work. { return(project.Properties.Where(p => p.PropertyType == Entity.PropertyTypes.Land).Select(p => p.Parcel).SelectMany(p => p.Evaluations).Max(p => (DateTime?)p.Date) ?? project.Properties.Where(p => p.PropertyType == Entity.PropertyTypes.Building).Select(p => p.Building).SelectMany(p => p.Evaluations).Max(b => (DateTime?)b.Date) ?? project.Properties.Where(p => p.PropertyType == Entity.PropertyTypes.Land).Select(p => p.Parcel).SelectMany(p => p.Fiscals).Max(b => (DateTime?)new DateTime(b.FiscalYear, 1, 1)) ?? project.Properties.Where(p => p.PropertyType == Entity.PropertyTypes.Building).Select(p => p.Building).SelectMany(p => p.Fiscals).Max(b => (DateTime?)new DateTime(b.FiscalYear, 1, 1)) ?? DateTime.UtcNow); }
/// <summary> /// Set the project workflow and status to the specified 'workflowStatus'. /// </summary> /// <param name="project"></param> /// <param name="workflowStatus"></param> /// <returns></returns> public static Entity.Project SetStatus(this Entity.Project project, Entity.WorkflowProjectStatus workflowStatus) { project.Workflow = workflowStatus.Workflow; project.WorkflowId = workflowStatus.WorkflowId; project.Status = workflowStatus.Status; project.StatusId = workflowStatus.StatusId; return(project); }
/// <summary> /// Transfer Project properties to a new agency with updated classifications /// </summary> /// <param name="context"></param> /// <param name="originalProject"></param> /// <param name="project"></param> /// <returns></returns> public static void TransferProjectProperties(this PimsContext context, Entity.Project originalProject, Entity.Project project) { originalProject.Properties.ForEach(p => { var matchingProperty = project.Properties.First(property => p.Id == property.Id); context.Update(p.UpdateProjectProperty(matchingProperty)); }); }
/// <summary> /// Add a tasks to the project. /// </summary> /// <param name="project"></param> /// <param name="tasks"></param> /// <returns></returns> public static Entity.Project AddTask(this Entity.Project project, params Entity.Task[] tasks) { tasks.ForEach(t => { project.Tasks.Add(new Entity.ProjectTask(project, t)); }); return(project); }
/// <summary> /// Add responses to the project. /// </summary> /// <param name="project"></param> /// <param name="responses"></param> /// <returns></returns> public static Entity.Project AddResponses(this Entity.Project project, params Entity.ProjectAgencyResponse[] responses) { responses.ForEach(r => { project.Responses.Add(r); }); return(project); }
/// <summary> /// Set the project workflow and status to the specified 'workflow' and 'status'. /// If the status is not part of the specified workflow it will throw an exception. /// </summary> /// <param name="context"></param> /// <param name="project"></param> /// <param name="workflowCode"></param> /// <param name="statusCode"></param> /// <returns></returns> public static PimsContext SetStatus(this PimsContext context, Entity.Project project, string workflowCode, string statusCode) { var workflow = context.Workflows.First(w => w.Code == workflowCode); var status = workflow.Status.First(s => s.Status.Code == statusCode); project.SetStatus(status); return(context); }
/// <summary> /// Determine if the project is closed or complete. /// </summary> /// <param name="project"></param> /// <returns></returns> public static bool IsProjectClosed(this Entity.Project project) { if (project.Status == null) { throw new ArgumentNullException(nameof(project.Status)); } return(project.Status.IsTerminal); }
/// <summary> /// Removes any responses from the original project that have been removed from the updated project. /// </summary> /// <param name="originalProject"></param> /// <param name="updatedProject"></param> public static void UpdateResponses(this Entity.Project originalProject, Entity.Project updatedProject) { var responseIds = updatedProject.Responses.Select(r => r.AgencyId).ToArray(); var removeResponses = originalProject.Responses.Where(r => !responseIds.Contains(r.AgencyId)).ToArray(); foreach (var response in removeResponses) { originalProject.Responses.Remove(response); } }
private string MajorActivity(Entity.Project project) { return(project.Status.Code switch { "DIS" => "Complete", "SPL-PM" => "Pre-Marketing", "SPL-M" => "On the Market", "PSL-CIP" => "Contract in Place", _ => project.Status.Name });
/// <summary> /// Add a parcel(s) to the project. /// </summary> /// <param name="project"></param> /// <param name="parcels"></param> /// <returns></returns> public static IEnumerable <Entity.ProjectProperty> AddProperty(this Entity.Project project, params Entity.Parcel[] parcels) { var result = new List <Entity.ProjectProperty>(); foreach (var p in parcels) { var pp = new Entity.ProjectProperty(project, p); project.Properties.Add(pp); result.Add(pp); } return(result); }
/// <summary> /// Add a building(s) to the project. /// </summary> /// <param name="project"></param> /// <param name="buildings"></param> /// <returns></returns> public static IEnumerable <Entity.ProjectProperty> AddProperty(this Entity.Project project, params Entity.Building[] buildings) { var result = new List <Entity.ProjectProperty>(); foreach (var b in buildings) { var pp = new Entity.ProjectProperty(project, b); project.Properties.Add(pp); result.Add(pp); } return(result); }
/// <summary> /// Transfer Project properties to a new agency with updated classifications /// </summary> /// <param name="context"></param> /// <param name="originalProject"></param> /// <param name="project"></param> /// <returns></returns> public static void TransferProjectProperties(this PimsContext context, Entity.Project originalProject, Entity.Project project) { var parentParcels = originalProject.GetSubdivisionParentParcels(); context.DisposeSubdivisionParentParcels(parentParcels); var propertiesWithNoSubdivisions = originalProject.Properties.Where(p => !parentParcels.Any(pp => p.Parcel?.Id == pp.Id)); propertiesWithNoSubdivisions.ForEach(p => { var matchingProperty = project.Properties.First(property => p.Id == property.Id); context.Update(p.UpdateProjectProperty(matchingProperty)); }); }
/// <summary> /// Add a snapshot for the project. /// </summary> /// <param name="project"></param> /// <param name="model"></param> /// <param name="metadata"></param> private void AddSnapshot(Entity.Project project, Model.ImportProjectModel model, Entity.Models.DisposalProjectSnapshotMetadata metadata) { var today = model.SnapshotOn ?? DateTime.Today.ToUniversalTime(); // Allows for all imports done on the same day to create the same snapshot time. metadata.NetProceeds = model.PriorNetProceeds; // Temporarily set it to the prior amount for the snapshot. var snapshot = new Entity.ProjectSnapshot(project) { SnapshotOn = today, Metadata = JsonSerializer.Serialize(metadata, _serializerOptions) }; project.Snapshots.Add(snapshot); metadata.NetProceeds = model.NetProceeds; // Set it back to the current value. }
/// <summary> /// Update the project financial values for the specified 'project'. /// Note - This requires that the referenced project includes all properties and their evaluations and fiscals. /// </summary> /// <param name="project"></param> public static void UpdateProjectFinancials(this Entity.Project project) { var date = project.GetProjectFinancialDate(); var year = date.Year; var fiscalYear = date.GetFiscalYear(); // TODO: Unclear if this should be 'Reported' or 'Actual', or current year. Using the most recent fiscal year based on financial data in project. // Sum up parcel values for the project year. // Sum up building values for the project year. project.NetBook = project.Properties.Where(p => p.PropertyType == Entity.PropertyTypes.Land).SelectMany(p => p.Parcel.Fiscals).Where(e => e.FiscalYear == fiscalYear && e.Key == Entity.FiscalKeys.NetBook).Sum(e => e.Value); project.NetBook += project.Properties.Where(p => p.PropertyType == Entity.PropertyTypes.Building).SelectMany(p => p.Building.Fiscals).Where(e => e.FiscalYear == fiscalYear && e.Key == Entity.FiscalKeys.NetBook).Sum(e => e.Value); project.Estimated = project.Properties.Where(p => p.PropertyType == Entity.PropertyTypes.Land).SelectMany(p => p.Parcel.Fiscals).Where(e => e.FiscalYear == fiscalYear && e.Key == Entity.FiscalKeys.Estimated).Sum(e => e.Value); project.Estimated += project.Properties.Where(p => p.PropertyType == Entity.PropertyTypes.Building).SelectMany(p => p.Building.Fiscals).Where(e => e.FiscalYear == fiscalYear && e.Key == Entity.FiscalKeys.Estimated).Sum(e => e.Value); project.Assessed = project.Properties.Where(p => p.PropertyType == Entity.PropertyTypes.Land).SelectMany(p => p.Parcel.Evaluations).Where(e => e.Date.Year == year && e.Key == Entity.EvaluationKeys.Assessed).Sum(e => e.Value); project.Assessed += project.Properties.Where(p => p.PropertyType == Entity.PropertyTypes.Building).SelectMany(p => p.Building.Evaluations).Where(e => e.Date.Year == year && e.Key == Entity.EvaluationKeys.Assessed).Sum(e => e.Value); }
/// <summary> /// Set Project properties' visibility to other agencies /// </summary> /// <param name="context"></param> /// <param name="project"></param> /// <param name="visibility"></param> public static void SetProjectPropertiesVisiblity(this PimsContext context, Entity.Project project, bool visibility) { project.Properties.ForEach(p => { if (p.BuildingId.HasValue) { p.Building.IsVisibleToOtherAgencies = visibility; context.Buildings.Update(p.Building); } else if (p.ParcelId.HasValue) { p.Parcel.IsVisibleToOtherAgencies = visibility; context.Parcels.Update(p.Parcel); } }); }
/// <summary> /// Append a new note to the project. /// Parse the 'noteType' value, or set it as 'General'. /// </summary> /// <param name="project"></param> /// <param name="noteType"></param> /// <param name="note"></param> private void AddNote(Entity.Project project, string noteType, string note) { if (!String.IsNullOrWhiteSpace(note)) { if (!Enum.TryParse(noteType, true, out Entity.NoteTypes type)) { type = noteType switch { "Property" => Entity.NoteTypes.Public, "PID" => Entity.NoteTypes.Public, "LandLegalDescription" => Entity.NoteTypes.Public, "Surplus Dec / Readiness Checklist" => Entity.NoteTypes.Public, "TBL - CBA" => Entity.NoteTypes.Public, "Enhanced Referral Comments" => Entity.NoteTypes.Private, "Business Case Comments" => Entity.NoteTypes.Private, "Business Case Submitted" => Entity.NoteTypes.Private, "Clearance Letter Issued" => Entity.NoteTypes.Private, "Interest from Enhanced Referral" => Entity.NoteTypes.AgencyInterest, "Appraisal Date" => Entity.NoteTypes.Appraisal, "Assessed & Appraised Comments" => Entity.NoteTypes.Appraisal, "Weekly Review" => Entity.NoteTypes.Reporting, _ => Entity.NoteTypes.General }; } var projectNote = project.Notes.FirstOrDefault(n => n.NoteType == type); if (projectNote == null) { project.Notes.Add(new Entity.ProjectNote(project, type, note)); } else { if (!String.IsNullOrWhiteSpace(projectNote.Note)) { projectNote.Note += $"{Environment.NewLine}{Environment.NewLine}{noteType}{Environment.NewLine}{note}"; } else { projectNote.Note = note; } } } }
/// Get a collection of responses that have changed from the original to the updated project. /// </summary> /// <param name="originalProject"></param> /// <param name="updatedProject"></param> /// <returns></returns> public static IEnumerable <ProjectAgencyResponse> GetResponseChanges(this Entity.Project originalProject, Entity.Project updatedProject) { if (updatedProject == null) { throw new ArgumentNullException(nameof(updatedProject)); } var responses = new List <ProjectAgencyResponse>(updatedProject.Responses.Count()); foreach (var response in updatedProject.Responses) { var originalResponse = originalProject.Responses.FirstOrDefault(r => r.ProjectId == response.ProjectId && r.AgencyId == response.AgencyId); if (originalResponse == null || originalResponse.Response != response.Response) { responses.Add(response); } } return(responses); }
/// <summary> /// Create a new instance of a Project. /// </summary> /// <param name="id"></param> /// <param name="agency"></param> /// <param name="tierLevel"></param> /// <param name="workflowStatus"></param> /// <returns></returns> public static Entity.Project CreateProject(int id, Entity.Agency agency = null, Entity.TierLevel tierLevel = null, Entity.WorkflowProjectStatus workflowStatus = null, Entity.ProjectRisk risk = null) { agency ??= EntityHelper.CreateAgency(id); tierLevel ??= EntityHelper.CreateTierLevel(id, "tierLevel"); risk ??= EntityHelper.CreateProjectRisk(id, "risk", "risk", 1); var status = workflowStatus?.Status ?? EntityHelper.CreateProjectStatus("Draft", "DR"); var workflow = workflowStatus?.Workflow ?? EntityHelper.CreateWorkflow(id, "Submit", "SUBMIT-DISPOSAL", new[] { status }); var user = CreateUser(Guid.NewGuid(), "project tester", "asasa", "asasa", null, agency); var project = new Entity.Project($"SPP-{id:00000}", $"test-{id}", tierLevel) { Id = id, ReportedFiscalYear = DateTime.UtcNow.GetFiscalYear(), ActualFiscalYear = DateTime.UtcNow.GetFiscalYear(), Risk = risk, RiskId = risk.Id, Agency = agency, AgencyId = agency.Id, Workflow = workflow, WorkflowId = workflow?.Id, Status = status, StatusId = status.Id, Description = $"description-{id}", CreatedBy = user, CreatedById = user.Id, CreatedOn = DateTime.UtcNow, UpdatedById = user.Id, UpdatedBy = user, UpdatedOn = DateTime.UtcNow, Metadata = "{\"offerAmount\": 123}", RowVersion = new byte[] { 12, 13, 14 } }; project.AddOrUpdateNote(Entity.NoteTypes.Public, $"publicNote-{id}"); project.AddOrUpdateNote(Entity.NoteTypes.Private, $"privateNote-{id}"); project.AddOrUpdateNote(Entity.NoteTypes.Appraisal, $"appraisedNote-{id}"); return(project); }
/// <summary> /// Copy the 'model' project properties into existing projects, or add new projects to PIMS. /// </summary> /// <param name="project"></param> /// <param name="model"></param> /// <param name="stopOnError"></param> /// <param name="defaults"></param> /// <returns></returns> private Entity.Project Merge(Entity.Project project, Model.ImportProjectModel model, bool stopOnError = true, string[] defaults = null) { try { // Use defaults if required if (defaults?.Any() ?? false) { var props = typeof(Model.ImportProjectModel).GetCachedProperties(); foreach (var kv in defaults) { var keyValue = kv.Trim().Split("="); if (keyValue.Length < 2) { throw new ArgumentException($"Argument '{kv}' is not valid."); } var prop = props.FirstOrDefault(p => String.Compare(p.Name, keyValue[0], true) == 0); var modelValue = prop?.GetValue(model); if (prop != null && modelValue == null || modelValue.Equals(prop.PropertyType.GetDefault())) { var value = Convert.ChangeType(keyValue[1], prop.PropertyType); prop.SetValue(model, value); } } } _logger.LogDebug($"Importing Project '{model.ProjectNumber}', agency:'{model.Agency}', workflow:'{model.Workflow}', status:'{model.Status}'"); project ??= new Entity.Project(); project.ProjectNumber = model.ProjectNumber; project.Name = model.Description; project.Description = model.Description; project.Manager = model.Manager; project.ActualFiscalYear = model.ActualFiscalYear; project.ReportedFiscalYear = model.ReportedFiscalYear; project.Agency = GetAgency(model.Agency); project.AgencyId = project.Agency.Id; project.Workflow = GetWorkflow(model.Workflow); project.WorkflowId = project.Workflow.Id; project.Status = GetStatus(model.Status); project.StatusId = project.Status.Id; project.TierLevel = GetTier(model.Estimated, project.Properties.Count()); // TODO: Need to import or link project properties. project.TierLevelId = project.TierLevel.Id; project.Risk = GetRisk(model.Risk); project.RiskId = project.Risk.Id; project.OcgFinancialStatement = model.OcgFinancialStatement; project.NetBook = model.NetBook; project.Estimated = model.Estimated; project.ProgramCost = model.ProgramCost; project.SalesCost = model.SalesCost; project.InterestComponent = model.InterestComponent; project.NetProceeds = model.NetProceeds; project.GainLoss = model.GainLoss; project.SaleWithLeaseInPlace = model.SaleWithLeaseInPlace; if (model.PriorNetProceeds.HasValue) { project.Snapshots.Add(new Entity.ProjectSnapshot(project) { NetProceeds = model.PriorNetProceeds.Value }); } project.MarketedOn = model.MarketedOn; project.CompletedOn = model.CompletedOn; project.PrivateNote = model.PrivateNote; // The data doesn't provide the purchasing agency information so the response will be the current owning agency. if (model.AgencyResponseDate.HasValue) { var response = project.Responses.FirstOrDefault(r => r.AgencyId == project.AgencyId); if (response == null) { project.Responses.Add(new Entity.ProjectAgencyResponse(project, project.Agency, Entity.NotificationResponses.Watch, model.AgencyResponseDate)); } else { response.Response = Entity.NotificationResponses.Watch; response.ReceivedOn = model.AgencyResponseDate ?? response.ReceivedOn; } } if (!String.IsNullOrWhiteSpace(model.FinancialNote)) { var financialNote = project.Notes.FirstOrDefault(n => n.NoteType == Entity.NoteTypes.Financial); if (financialNote == null) { project.Notes.Add(new Entity.ProjectNote(project, Entity.NoteTypes.Financial, model.FinancialNote)); } else { financialNote.Note = model.FinancialNote; } } // Add tasks if they haven't been added already. if (!project.Tasks.Any()) { // Assumption we'll need to add all tasks from Submit to SPL. var tasks = _service.Task.GetForWorkflow("SUBMIT-DISPOSAL"); tasks = tasks.Concat(_service.Task.GetForWorkflow("ASSESS-DISPOSAL")); tasks = tasks.Concat(_service.Task.GetForWorkflow("ERP")); tasks = tasks.Concat(_service.Task.GetForWorkflow("SPL")); project.AddTask(tasks.DistinctBy(t => t.Id).ToArray()); project.Tasks.ForEach(t => { t.IsCompleted = !t.Task.IsOptional; }); } _logger.LogDebug($"Parsed project '{project.ProjectNumber}' - '{project.Status.Code}'", project); return(project); } catch (Exception ex) { _logger.LogError(ex, $"Failed to parse this project while importing '{model.ProjectNumber}' - {ex.Message}"); if (stopOnError) { throw; } return(null); } }