/// <summary> /// Method to determine available labour based on filters and take it if requested. /// </summary> /// <param name="request">Resource request details</param> /// <param name="removeFromResource">Determines if only calculating available labour or labour removed</param> /// <param name="callingModel">Model calling this method</param> /// <param name="resourceHolder">Location of resource holder</param> /// <param name="partialAction">Action on partial resources available</param> /// <returns></returns> public static double TakeLabour(ResourceRequest request, bool removeFromResource, IModel callingModel, ResourcesHolder resourceHolder, OnPartialResourcesAvailableActionTypes partialAction) { double amountProvided = 0; double amountNeeded = request.Required; LabourFilterGroup current = request.FilterDetails.OfType <LabourFilterGroup>().FirstOrDefault() as LabourFilterGroup; LabourRequirement lr; if (current != null) { if (current.Parent is LabourRequirement) { lr = current.Parent as LabourRequirement; } else { // coming from Transmutation request lr = new LabourRequirement() { ApplyToAll = false, MaximumPerPerson = 1000, MinimumPerPerson = 0 }; } } else { lr = Apsim.Children(callingModel, typeof(LabourRequirement)).FirstOrDefault() as LabourRequirement; } int currentIndex = 0; if (current == null) { // no filtergroup provided so assume any labour current = new LabourFilterGroup(); } request.ResourceTypeName = "Labour"; ResourceRequest removeRequest = new ResourceRequest() { ActivityID = request.ActivityID, ActivityModel = request.ActivityModel, AdditionalDetails = request.AdditionalDetails, AllowTransmutation = request.AllowTransmutation, Available = request.Available, FilterDetails = request.FilterDetails, Provided = request.Provided, Reason = request.Reason, Required = request.Required, Resource = request.Resource, ResourceType = request.ResourceType, ResourceTypeName = request.ResourceTypeName }; // start with top most LabourFilterGroup while (current != null && amountProvided < amountNeeded) { List <LabourType> items = (resourceHolder.GetResourceGroupByType(request.ResourceType) as Labour).Items; items = items.Where(a => (a.LastActivityRequestID != request.ActivityID) || (a.LastActivityRequestID == request.ActivityID && a.LastActivityRequestAmount < lr.MaximumPerPerson)).ToList(); items = items.Filter(current as Model); // search for people who can do whole task first while (amountProvided < amountNeeded && items.Where(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson) >= request.Required).Count() > 0) { // get labour least available but with the amount needed LabourType lt = items.Where(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson) >= request.Required).OrderBy(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson)).FirstOrDefault(); double amount = Math.Min(amountNeeded - amountProvided, lt.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson)); // limit to max allowed per person amount = Math.Min(amount, lr.MaximumPerPerson); // limit to min per person to do activity if (amount < lr.MinimumPerPerson) { request.Reason = "Min labour limit"; return(amountProvided); } amountProvided += amount; removeRequest.Required = amount; if (removeFromResource) { lt.LastActivityRequestID = request.ActivityID; lt.LastActivityRequestAmount = amount; lt.Remove(removeRequest); request.Provided += removeRequest.Provided; request.Value += request.Provided * lt.PayRate(); } } // if still needed and allow partial resource use. if (partialAction == OnPartialResourcesAvailableActionTypes.UseResourcesAvailable) { if (amountProvided < amountNeeded) { // then search for those that meet criteria and can do part of task foreach (LabourType item in items.Where(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson) >= 0).OrderByDescending(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson))) { if (amountProvided >= amountNeeded) { break; } double amount = Math.Min(amountNeeded - amountProvided, item.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumPerPerson)); // limit to max allowed per person amount = Math.Min(amount, lr.MaximumPerPerson); // limit to min per person to do activity if (amount >= lr.MinimumPerPerson) { amountProvided += amount; removeRequest.Required = amount; if (removeFromResource) { if (item.LastActivityRequestID != request.ActivityID) { item.LastActivityRequestAmount = 0; } item.LastActivityRequestID = request.ActivityID; item.LastActivityRequestAmount += amount; item.Remove(removeRequest); request.Provided += removeRequest.Provided; request.Value += request.Provided * item.PayRate(); } } else { currentIndex = request.FilterDetails.Count; } } } } currentIndex++; if (current.Children.OfType <LabourFilterGroup>().Count() > 0) { current = current.Children.OfType <LabourFilterGroup>().FirstOrDefault(); } else { current = null; } } // report amount gained. return(amountProvided); }
/// <summary> /// Method to determine available labour based on filters and take it if requested. /// </summary> /// <param name="request">Resource request details</param> /// <param name="removeFromResource">Determines if only calculating available labour or labour removed</param> /// <param name="callingModel">Model calling this method</param> /// <param name="resourceHolder">Location of resource holder</param> /// <param name="partialAction">Action on partial resources available</param> /// <returns></returns> public static double TakeLabour(ResourceRequest request, bool removeFromResource, IModel callingModel, ResourcesHolder resourceHolder, OnPartialResourcesAvailableActionTypes partialAction) { double amountProvided = 0; double amountNeeded = request.Required; LabourFilterGroup current = request.FilterDetails.OfType <LabourFilterGroup>().FirstOrDefault(); LabourRequirement lr; if (current != null) { if (current.Parent is LabourRequirement) { lr = current.Parent as LabourRequirement; } else { // coming from Transmutation request lr = new LabourRequirement() { LimitStyle = LabourLimitType.AsDaysRequired, ApplyToAll = false, MaximumPerGroup = 10000, MaximumPerPerson = 1000, MinimumPerPerson = 0 } }; } else { lr = callingModel.FindAllChildren <LabourRequirement>().FirstOrDefault(); } lr.CalculateLimits(amountNeeded); amountNeeded = Math.Min(amountNeeded, lr.MaximumDaysPerGroup); request.Required = amountNeeded; // may need to reduce request here or shortfalls will be triggered int currentIndex = 0; if (current == null) { // no filtergroup provided so assume any labour current = new LabourFilterGroup(); } request.ResourceTypeName = "Labour"; ResourceRequest removeRequest = new ResourceRequest() { ActivityID = request.ActivityID, ActivityModel = request.ActivityModel, AdditionalDetails = request.AdditionalDetails, AllowTransmutation = request.AllowTransmutation, Available = request.Available, FilterDetails = request.FilterDetails, Provided = request.Provided, Category = request.Category, RelatesToResource = request.RelatesToResource, Required = request.Required, Resource = request.Resource, ResourceType = request.ResourceType, ResourceTypeName = (request.Resource is null? "":(request.Resource as CLEMModel).NameWithParent) }; // start with top most LabourFilterGroup while (current != null && amountProvided < amountNeeded) { IEnumerable <LabourType> items = resourceHolder.FindResource <Labour>().Items; items = items.Where(a => (a.LastActivityRequestID != request.ActivityID) || (a.LastActivityRequestID == request.ActivityID && a.LastActivityRequestAmount < lr.MaximumDaysPerPerson)); items = current.Filter(items); // search for people who can do whole task first while (amountProvided < amountNeeded && items.Where(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumDaysPerPerson) >= request.Required).Any()) { // get labour least available but with the amount needed LabourType lt = items.Where(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumDaysPerPerson) >= request.Required).OrderBy(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumDaysPerPerson)).FirstOrDefault(); double amount = Math.Min(amountNeeded - amountProvided, lt.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumDaysPerPerson)); // limit to max allowed per person amount = Math.Min(amount, lr.MaximumDaysPerPerson); // limit to min per person to do activity if (amount < lr.MinimumPerPerson) { request.Category = "Min labour limit"; return(amountProvided); } amountProvided += amount; removeRequest.Required = amount; if (removeFromResource) { lt.LastActivityRequestID = request.ActivityID; lt.LastActivityRequestAmount = amount; lt.Remove(removeRequest); request.Provided += removeRequest.Provided; request.Value += request.Provided * lt.PayRate(); } } // if still needed and allow partial resource use. if (partialAction == OnPartialResourcesAvailableActionTypes.UseResourcesAvailable) { if (amountProvided < amountNeeded) { // then search for those that meet criteria and can do part of task foreach (LabourType item in items.Where(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumDaysPerPerson) >= 0).OrderByDescending(a => a.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumDaysPerPerson))) { if (amountProvided >= amountNeeded) { break; } double amount = Math.Min(amountNeeded - amountProvided, item.LabourCurrentlyAvailableForActivity(request.ActivityID, lr.MaximumDaysPerPerson)); // limit to max allowed per person amount = Math.Min(amount, lr.MaximumDaysPerPerson); // limit to min per person to do activity if (amount >= lr.MinimumDaysPerPerson) { amountProvided += amount; removeRequest.Required = amount; if (removeFromResource) { if (item.LastActivityRequestID != request.ActivityID) { item.LastActivityRequestAmount = 0; } item.LastActivityRequestID = request.ActivityID; item.LastActivityRequestAmount += amount; item.Remove(removeRequest); request.Provided += removeRequest.Provided; request.Value += request.Provided * item.PayRate(); } } else { currentIndex = request.FilterDetails.Count; } } } } currentIndex++; var currentFilterGroups = current.FindAllChildren <LabourFilterGroup>(); if (currentFilterGroups.Any()) { current = currentFilterGroups.FirstOrDefault(); } else { current = null; } } // report amount gained. return(amountProvided); }