746 lines
25 KiB
C#
746 lines
25 KiB
C#
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
|
using Sarand.BusinessMgr.Net.Standard.Base.Core;
|
|
using Sarand.Net.Standard.Base.Bus;
|
|
using Sarand.Net.Standard.Base.CodeTypes;
|
|
using Sarand.Net.Standard.Base.Core;
|
|
using Sarand.Net.Standard.Base.Utils;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Sarand.BusinessMgr.Net.Standard.Base.Bus
|
|
{
|
|
//=======================================================================
|
|
public class SbmCostEstimate : SbmHierarchyItem
|
|
{
|
|
//-------------------------------------------------------------------
|
|
// Columns.
|
|
public enum Column
|
|
{
|
|
Hidden,
|
|
Pos,
|
|
Id,
|
|
// CostEstimate.
|
|
// Common properties.
|
|
Notes,
|
|
Status,
|
|
Status_Ind,
|
|
Status_Desc,
|
|
CreateOn_Date,
|
|
UpdateOn_Date,
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
/// <summary>Properties other than common.</summary>
|
|
public class DataProperties
|
|
{
|
|
public Sbm.CostEstimateCategory CostEstimateCategory { get; set; } = Sbm.CostEstimateCategory.Unknown;
|
|
public String Type { get; set; } = "";
|
|
public decimal Time { get; set; } = 0.0m;
|
|
public decimal Rate { get; set; } = 0.0m;
|
|
public decimal Cost { get; set; } = 0.0m;
|
|
public ushort Markup { get; set; } = 0;
|
|
public ushort MinQuantity { get; set; } = 0;
|
|
public ushort CodeSeq { get; set; } = 0;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Data.
|
|
public new class Data : SacsHierarchyItem.Data
|
|
{
|
|
// Data members.
|
|
private readonly DataProperties data = new DataProperties();
|
|
// Construction.
|
|
public Data()
|
|
{
|
|
Status = DefaultStatus;
|
|
}
|
|
|
|
public Sbm.CostEstimateCategory CostEstimateCategory
|
|
{
|
|
get { return data.CostEstimateCategory; }
|
|
set { CheckChanged((int)data.CostEstimateCategory, (int)value); data.CostEstimateCategory = value; }
|
|
}
|
|
public String Type
|
|
{
|
|
get { return data.Type; }
|
|
set { CheckChanged(data.Type, value); data.Type = value; }
|
|
}
|
|
public decimal Time
|
|
{
|
|
get { return data.Time; }
|
|
set { CheckChanged(data.Time, value); data.Time = value; }
|
|
}
|
|
public decimal Rate
|
|
{
|
|
get { return data.Rate; }
|
|
set { CheckChanged(data.Rate, value); data.Rate = value; }
|
|
}
|
|
public decimal Cost
|
|
{
|
|
get { return data.Cost; }
|
|
set { CheckChanged(data.Cost, value); data.Time = value; }
|
|
}
|
|
public ushort Markup
|
|
{
|
|
get { return data.Markup; }
|
|
set { CheckChanged(data.Markup, value); data.Markup = value; }
|
|
}
|
|
public ushort MinQuantity
|
|
{
|
|
get { return data.MinQuantity; }
|
|
set { CheckChanged(data.MinQuantity, value); data.MinQuantity = value; }
|
|
}
|
|
public ushort CodeSeq
|
|
{
|
|
get { return data.CodeSeq; }
|
|
set { CheckChanged(data.CodeSeq, value); data.CodeSeq = value; }
|
|
}
|
|
|
|
internal void PopulateFrom(Data data)
|
|
{
|
|
CostEstimateCategory = data.CostEstimateCategory;
|
|
Type = data.Type;
|
|
Time = data.Time;
|
|
Rate = data.Rate;
|
|
Cost = data.Cost;
|
|
Markup = data.Markup;
|
|
MinQuantity = data.MinQuantity;
|
|
CodeSeq = data.CodeSeq;
|
|
base.PopulateFrom(data);
|
|
}
|
|
public Data Clone()
|
|
{
|
|
Data data = new Data();
|
|
data.PopulateFrom(this);
|
|
return data;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Data members.
|
|
private readonly Data data = new Data();
|
|
// Static Definition.
|
|
#pragma warning disable IDE0051 // Remove unused private members
|
|
private static readonly String delimArchiveOld = "#~#";
|
|
#pragma warning restore IDE0051 // Remove unused private members
|
|
private static readonly String delimArchive = "#~#";
|
|
|
|
//-------------------------------------------------------------------
|
|
// Construction.
|
|
public SbmCostEstimate(Sbm.CostEstimateCategory category) : base()
|
|
{
|
|
CostEstimateCategory = category;
|
|
Status = DefaultStatus;
|
|
}
|
|
// Conversion from ItemLink.
|
|
public SbmCostEstimate(String archive) : base()
|
|
{
|
|
String[] data = archive.Split(new String[] { delimArchive }, StringSplitOptions.None);
|
|
CostEstimateCategory = (Sbm.CostEstimateCategory)(data.Length > 0 ? StringUtil.ToShort(data[0]) : 0);
|
|
Type = (data.Length > 1 ? data[1] : "");
|
|
Time = (data.Length > 2 ? StringUtil.ToDecimal(data[2]) : 0.0m);
|
|
Rate = (data.Length > 3 ? StringUtil.ToDecimal(data[3]) : 0.0m);
|
|
Cost = (data.Length > 4 ? StringUtil.ToDecimal(data[4]) : 0.0m);
|
|
Markup = (ushort)(data.Length > 5 ? StringUtil.ToInt(data[5]) : 0);
|
|
MinQuantity = (ushort)(data.Length > 6 ? StringUtil.ToInt(data[6]) : 0);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// CostEstimate status.
|
|
private static readonly List<SaStatus> costEstimateStatuses = new List<SaStatus>()
|
|
{
|
|
SaStatus.Active,
|
|
SaStatus.Disabled,
|
|
};
|
|
public static SaStatus DefaultStatus { get { return SaStatus.Current; } }
|
|
public static List<SaStatus> CostEstimateStatuses { get { return new List<SaStatus>(costEstimateStatuses); } }
|
|
public static DataValue.TriStateType GetStatusTriState(SaStatus status)
|
|
{
|
|
switch (status)
|
|
{
|
|
case SaStatus.Active:
|
|
return DataValue.TriStateType.Green;
|
|
case SaStatus.OnHold:
|
|
return DataValue.TriStateType.Amber;
|
|
case SaStatus.Disabled:
|
|
return DataValue.TriStateType.Red;
|
|
case SaStatus.Deleted:
|
|
return DataValue.TriStateType.Purple;
|
|
}
|
|
return DataValue.TriStateType.Unknown;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Utilities.
|
|
public override bool ItemHasId() { return false; }
|
|
|
|
public List<TypeCodeDesc> GetCategories(Sbm.CostEstimateCategoryType categoryType, bool includeCommodityCode)
|
|
{
|
|
List<TypeCodeDesc> categories = new List<TypeCodeDesc>();
|
|
if (includeCommodityCode)
|
|
categories.Add(Sbm.CostEstimateCategory.CommodityCost, "CCU", "Material per unit");
|
|
categories.Add(Sbm.CostEstimateCategory.CostPerUnit, "CPU", "Cost per unit");
|
|
categories.Add(Sbm.CostEstimateCategory.CostPerJob, "CPJ", "Cost per job");
|
|
categories.Add(Sbm.CostEstimateCategory.LabourPerUnit, "LPU", "Labour per unit");
|
|
categories.Add(Sbm.CostEstimateCategory.LabourPerJob, "LPJ", "Labour per job");
|
|
// Remove unneccessary categories from the list:
|
|
if (categoryType != Sbm.CostEstimateCategoryType.Any)
|
|
{
|
|
foreach (TypeCodeDesc category in categories)
|
|
{
|
|
if (categoryType == Sbm.CostEstimateCategoryType.Cost && IsLabour((Sbm.CostEstimateCategory)category.Type)) categories.Clear();
|
|
else if (categoryType == Sbm.CostEstimateCategoryType.Labour && !IsLabour((Sbm.CostEstimateCategory)category.Type)) categories.Clear();
|
|
}
|
|
}
|
|
return categories;
|
|
}
|
|
|
|
// Cost estimate types.
|
|
public Sbm.ApplCodeType GetCodeTypeForCategory(Sbm.CostEstimateCategory category)
|
|
{
|
|
if (category == Sbm.CostEstimateCategory.CostPerUnit) return Sbm.ApplCodeType.CostPerUnit;
|
|
if (category == Sbm.CostEstimateCategory.CostPerJob) return Sbm.ApplCodeType.CostPerJob;
|
|
if (category == Sbm.CostEstimateCategory.LabourPerUnit) return Sbm.ApplCodeType.LabourPerUnit;
|
|
if (category == Sbm.CostEstimateCategory.LabourPerJob) return Sbm.ApplCodeType.LabourPerJob;
|
|
return Sbm.ApplCodeType.Unknown;
|
|
}
|
|
|
|
// Matching.
|
|
public bool MatchesByCategoryType(Sbm.CostEstimateCategory category, String type)
|
|
{
|
|
if (CostEstimateCategory != category) return false;
|
|
if (Type != type) return false;
|
|
return true;
|
|
}
|
|
|
|
// Cloning - make a copy of the item.
|
|
public SbmCostEstimate Clone()
|
|
{
|
|
SbmCostEstimate costEstimateCost = new SbmCostEstimate(CostEstimateCategory);
|
|
costEstimateCost.SetData(GetData().Clone());
|
|
return costEstimateCost;
|
|
}
|
|
|
|
// Conversion.
|
|
public String Conversion()
|
|
{
|
|
String archive = "";
|
|
archive += CostEstimateCategory.ToString() + delimArchive;
|
|
archive += Type.ToString() + delimArchive;
|
|
archive += StringUtil.FormatNumeric(Time, 4) + delimArchive;
|
|
archive += Rate.ToString() + delimArchive;
|
|
archive += Cost.ToString() + delimArchive;
|
|
archive += Markup.ToString() + delimArchive;
|
|
archive += MinQuantity.ToString() + delimArchive;
|
|
return archive;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Validation.
|
|
public bool IsValid()
|
|
{
|
|
if (!IsValidCategory(CostEstimateCategory)) return false;
|
|
if (!IsValidType()) return false;
|
|
if (!IsValidTime()) return false;
|
|
if (!IsValidRate()) return false;
|
|
if (!IsValidCost()) return false;
|
|
return true;
|
|
}
|
|
|
|
public bool IsValidCategory(Sbm.CostEstimateCategory category)
|
|
{
|
|
if (category < Sbm.CostEstimateCategory.CommodityCost) return false;
|
|
if (category > Sbm.CostEstimateCategory.LabourPerJob) return false;
|
|
return true;
|
|
}
|
|
|
|
public bool IsValidType()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public bool IsValidTime()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public bool IsValidRate()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public bool IsValidCost()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Properties.
|
|
public Data GetData() { return data.Clone(); }
|
|
public void SetData(Data data)
|
|
{
|
|
// Properties that cannot be changed if previously specified.
|
|
if (SysId.IsValid(Id) && SysId.ToInt(Id) != SysId.ToInt(data.Id))
|
|
throw new NotSupportedException($"{SysUtil.Fn} Id cannot be changed");
|
|
// Populate individual properties to ensure that 'Changed' indicator is kept up to date.
|
|
this.data.PopulateFrom(data);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Attributes.
|
|
public Sbm.CostEstimateCategory CostEstimateCategory
|
|
{
|
|
get { return data.CostEstimateCategory; }
|
|
set { data.CostEstimateCategory = value; }
|
|
}
|
|
public bool IsLabour()
|
|
{
|
|
return IsLabour(CostEstimateCategory);
|
|
}
|
|
public bool IsLabour(Sbm.CostEstimateCategory category)
|
|
{
|
|
switch (category)
|
|
{
|
|
case Sbm.CostEstimateCategory.LabourPerJob:
|
|
case Sbm.CostEstimateCategory.LabourPerUnit:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
public bool IsUnitCost()
|
|
{
|
|
return IsUnitCost(CostEstimateCategory);
|
|
}
|
|
public bool IsUnitCost(Sbm.CostEstimateCategory category)
|
|
{
|
|
switch (category)
|
|
{
|
|
case Sbm.CostEstimateCategory.CommodityCost:
|
|
case Sbm.CostEstimateCategory.CostPerUnit:
|
|
case Sbm.CostEstimateCategory.LabourPerUnit:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
public bool IsJobCost()
|
|
{
|
|
return IsJobCost(CostEstimateCategory);
|
|
}
|
|
public bool IsJobCost(Sbm.CostEstimateCategory category)
|
|
{
|
|
switch (category)
|
|
{
|
|
case Sbm.CostEstimateCategory.CostPerJob:
|
|
case Sbm.CostEstimateCategory.LabourPerJob:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
public String Type
|
|
{
|
|
get { return data.Type; }
|
|
set { data.Type = value; }
|
|
}
|
|
public decimal Time
|
|
{
|
|
get { return data.Time; }
|
|
set { data.Time = value; }
|
|
}
|
|
public decimal Rate
|
|
{
|
|
get { return data.Rate; }
|
|
set { data.Rate = value; }
|
|
}
|
|
public decimal Cost
|
|
{
|
|
get { return data.Cost; }
|
|
set { data.Time = value; }
|
|
}
|
|
public ushort Markup
|
|
{
|
|
get { return data.Markup; }
|
|
set { data.Markup = value; }
|
|
}
|
|
public ushort MinQuantity
|
|
{
|
|
get { return data.MinQuantity; }
|
|
set { data.MinQuantity = value; }
|
|
}
|
|
public ushort CodeSeq
|
|
{
|
|
get { return data.CodeSeq; }
|
|
set { data.CodeSeq = value; }
|
|
}
|
|
|
|
// Calculated costs.
|
|
public decimal GetCostPerUnit(decimal quantity)
|
|
{
|
|
decimal costPerUnit = Cost;
|
|
if (IsLabour())
|
|
costPerUnit = Time * Rate;
|
|
if (IsJobCost())
|
|
{
|
|
if (quantity != 0.0m)
|
|
costPerUnit /= quantity;
|
|
else
|
|
costPerUnit = 0m;
|
|
}
|
|
// Apply the markup %
|
|
if (Markup > 0)
|
|
costPerUnit *= 1 + (Markup / 100.0m);
|
|
// Round to 2 decimals.
|
|
long costPerUnit100 = (long)((costPerUnit * 100.0m) + 0.5m);
|
|
costPerUnit = costPerUnit100 / 100.0m;
|
|
return costPerUnit;
|
|
}
|
|
|
|
public decimal GetCostPerJob(decimal quantity)
|
|
{
|
|
// Check for 'Cost per job' categories.
|
|
if (IsJobCost(CostEstimateCategory))
|
|
{
|
|
if (IsLabour())
|
|
return Time * Rate;
|
|
return Cost;
|
|
}
|
|
// Cost is 'per unit'.
|
|
decimal costPerJob = GetCostPerUnit(quantity) * quantity;
|
|
return costPerJob;
|
|
}
|
|
//-------------------------------------------------------------------
|
|
}
|
|
|
|
//=======================================================================
|
|
// C o s t E s t i m a t e D a t a L i s t
|
|
//=======================================================================
|
|
public static class SbmCostEstimateList
|
|
{
|
|
/// <summary>Check whether item exists/locate in list with specified Id.</summary>
|
|
//-------------------------------------------------------------------
|
|
// Find in List.
|
|
public static bool Find(this List<SbmCostEstimate> values, SbmCostEstimate costEstimate)
|
|
{
|
|
if (costEstimate == null) return false;
|
|
return values.Any(item => item == costEstimate);
|
|
}
|
|
|
|
public static bool FindByCategoryType(this List<SbmCostEstimate> values, Sbm.CostEstimateCategory category, String type)
|
|
{
|
|
if (category < 0 && StringUtil.IsEmpty(type)) return false;
|
|
return values.Any(item => item.MatchesByCategoryType(category, type));
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
public static void SetCommodityCosts(this List<SbmCostEstimate> values, ISbmUserOrganisationContext userContext, SbmInternals internals, SbmCommodity commodity, decimal quantity)
|
|
{
|
|
// Retrieve costs from commodity and remove unwanted costs (for other quantities).
|
|
List<SbmCostEstimate> costEstimatesCommodity = internals.CommodityMgr.GetCommodityCostEstimates(commodity);
|
|
costEstimatesCommodity.ConsolidateByQuantity(quantity);
|
|
|
|
// If there are cost estimates remaining, replace the contents of this list.
|
|
if (costEstimatesCommodity != null && !StringUtil.IsEmpty(costEstimatesCommodity.ToString()))
|
|
{
|
|
// Make a note of the current list of estimates and replace with those from the commodity.
|
|
List<SbmCostEstimate> costEstimatesCostSave = costEstimatesCommodity;
|
|
costEstimatesCommodity.Clear();
|
|
|
|
// Copy associated details from previous list.
|
|
for (int est = 1; est <= values.Count; est++)
|
|
{
|
|
SbmCostEstimate costEstimate = values[est];
|
|
if (costEstimatesCostSave.FindByCategoryType(costEstimate.CostEstimateCategory, costEstimate.Type))
|
|
{
|
|
costEstimate.Markup = values[est].Markup;
|
|
if (costEstimate.IsLabour())
|
|
costEstimate.Rate = values[est].Rate;
|
|
costEstimate.Notes = values[est].Notes;
|
|
}
|
|
}
|
|
|
|
// Clean-up:
|
|
costEstimatesCostSave.Clear();
|
|
}
|
|
else
|
|
{
|
|
// Commodity has no costs - just use the cost price (if specified).
|
|
decimal cost = commodity.CostPrice;
|
|
if (cost > 0)
|
|
{
|
|
bool bCommodityCostFound = false;
|
|
for (int i = 1; !bCommodityCostFound && i <= values.Count; i++)
|
|
{
|
|
SbmCostEstimate costEstimateCost = values[i];
|
|
if (costEstimateCost == null) continue;
|
|
if (costEstimateCost.CostEstimateCategory == Sbm.CostEstimateCategory.CommodityCost)
|
|
{
|
|
costEstimateCost.Cost = cost;
|
|
bCommodityCostFound = true;
|
|
}
|
|
}
|
|
if (!bCommodityCostFound)
|
|
{
|
|
// Create a new entry for the commodity cost.
|
|
SbmCostEstimate costEstimateCost = new SbmCostEstimate(Sbm.CostEstimateCategory.CommodityCost.ToString());
|
|
costEstimateCost.Cost = cost;
|
|
costEstimatesCommodity.Add(costEstimateCost);
|
|
costEstimatesCommodity.Sort();
|
|
costEstimateCost = null; // Now owned by the list.
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean-up:
|
|
if (costEstimatesCommodity != null)
|
|
{
|
|
costEstimatesCommodity.Clear();
|
|
}
|
|
}
|
|
public static void ConsolidateByQuantity(this List<SbmCostEstimate> values, decimal quantity)
|
|
{
|
|
//if (!this || IsEmpty()) return.
|
|
if (quantity < 1) quantity = 1.0m;
|
|
ushort iQuantity = (ushort)(quantity + 0.5m);
|
|
|
|
// Repeat for each cost in the list.
|
|
for (int est = 1; est <= values.Count; est++)
|
|
{
|
|
// Check if this cost estimate's min qty is higher than the specified quantity.
|
|
SbmCostEstimate costEstimateCost = values[est];
|
|
Sbm.CostEstimateCategory category = costEstimateCost.CostEstimateCategory;
|
|
ushort iMinQty = costEstimateCost.MinQuantity;
|
|
if (iMinQty > iQuantity)
|
|
{
|
|
// Never use a cost estimate where the min qty is higher than the required qty.
|
|
costEstimateCost = null;
|
|
est--;
|
|
continue;
|
|
}
|
|
|
|
// Compare to each of the other costs in the list:
|
|
for (int costCmd = 1; costEstimateCost != null && costCmd <= values.Count; costCmd++)
|
|
{
|
|
|
|
// Ignore the same commodity.
|
|
SbmCostEstimate costEstimateCommodity = values[costCmd];
|
|
if (costEstimateCommodity == costEstimateCost) continue;
|
|
|
|
// Only interested if the category and type are the same:
|
|
if (category == costEstimateCommodity.CostEstimateCategory && costEstimateCost.Type == costEstimateCommodity.Type)
|
|
{
|
|
ushort iMinQtyCmp = costEstimateCommodity.MinQuantity;
|
|
// Commodity costs are banded by minimum quantity.
|
|
// Costs are retained where their minimum quantity is the
|
|
// highest value less than or equal to the specified quantity:
|
|
// eg. Min qty: 1,10,20, Qty = 15, retain min qty 10.
|
|
if (iMinQty < iMinQtyCmp)
|
|
{
|
|
if (iQuantity < iMinQtyCmp)
|
|
{
|
|
costEstimateCommodity = null;
|
|
costCmd--;
|
|
continue;
|
|
}
|
|
else if (iQuantity >= iMinQtyCmp)
|
|
{
|
|
costEstimateCost = null;
|
|
est--;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//-------------------------------------------------------------------
|
|
// Seq/Compare.
|
|
public static void SetTypeCodeSequence(this List<SbmCostEstimate> values, List<SaCodeDfn> costPerUnits, List<SaCodeDfn> costPerJobs, List<SaCodeDfn> labourPerUnits, List<SaCodeDfn> labourPerJobs)
|
|
{
|
|
foreach (SbmCostEstimate costEstimate in values)
|
|
{
|
|
// Identify the corresponding list of code definitions.
|
|
List<SaCodeDfn> codes = null;
|
|
switch (costEstimate.CostEstimateCategory)
|
|
{
|
|
case Sbm.CostEstimateCategory.CostPerUnit: codes = costPerUnits; break;
|
|
case Sbm.CostEstimateCategory.CostPerJob: codes = costPerJobs; break;
|
|
case Sbm.CostEstimateCategory.LabourPerUnit: codes = labourPerUnits; break;
|
|
case Sbm.CostEstimateCategory.LabourPerJob: codes = labourPerJobs; break;
|
|
}
|
|
if (codes != null && codes.Exists(item => item.Code.Equals(costEstimate.Type)))
|
|
costEstimate.CodeSeq = codes.Find(item => item.Code.Equals(costEstimate.CodeSeq)).Sequence;
|
|
}
|
|
}
|
|
public static int CompareCatType(this List<SbmCostEstimate> values, SbmCostEstimate costEstimate1, SbmCostEstimate costEstimate2)
|
|
{
|
|
if (costEstimate1 == null || costEstimate2 == null) return 0;
|
|
if (costEstimate1 == costEstimate2) return 0;
|
|
|
|
// Commodity costs always appear at the top of the list:
|
|
if (costEstimate1.CostEstimateCategory != Sbm.CostEstimateCategory.CommodityCost && costEstimate1.CostEstimateCategory == Sbm.CostEstimateCategory.CommodityCost) return 1;
|
|
if (costEstimate1.CostEstimateCategory == Sbm.CostEstimateCategory.CommodityCost && costEstimate1.CostEstimateCategory != Sbm.CostEstimateCategory.CommodityCost) return -1;
|
|
if (costEstimate1.CostEstimateCategory == Sbm.CostEstimateCategory.CommodityCost && costEstimate1.CostEstimateCategory == Sbm.CostEstimateCategory.CommodityCost)
|
|
{
|
|
return costEstimate1.Type.CompareTo(costEstimate2.Type);
|
|
}
|
|
|
|
// Labour estimates always appear below cost estimates.
|
|
if (costEstimate1.IsLabour() && !costEstimate2.IsLabour()) return 1;
|
|
if (!costEstimate1.IsLabour() && costEstimate2.IsLabour()) return -1;
|
|
|
|
// Estimates are the same type (cost or labour).
|
|
// Compare the sequence derived from the typed code.
|
|
if (costEstimate1.CodeSeq > costEstimate2.CodeSeq) return 1;
|
|
if (costEstimate1.CodeSeq < costEstimate2.CodeSeq) return -1;
|
|
|
|
// Code sequences are the same - compare the type code.
|
|
if (costEstimate1.Type != costEstimate2.Type)
|
|
return costEstimate1.Type.CompareTo(costEstimate2.Type);
|
|
|
|
// Everything else being equal, sort per unit before per job.
|
|
if (costEstimate1.IsJobCost() && !costEstimate2.IsJobCost()) return 1;
|
|
if (!costEstimate1.IsJobCost() && costEstimate2.IsJobCost()) return -1;
|
|
|
|
if (costEstimate1.CostEstimateCategory > costEstimate2.CostEstimateCategory) return 1;
|
|
if (costEstimate1.CostEstimateCategory < costEstimate2.CostEstimateCategory) return -1;
|
|
int costEstimateCompare = costEstimate1.Type.CompareTo(costEstimate2.Type);
|
|
if (costEstimateCompare != 0) return costEstimateCompare;
|
|
if (costEstimate1.GetCostPerUnit(1.0m) > costEstimate2.GetCostPerUnit(1.0m)) return 1;
|
|
if (costEstimate1.CostEstimateCategory == costEstimate2.CostEstimateCategory && costEstimate1.Type == costEstimate2.Type) return 0;
|
|
return -1;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Sort - by category and type.
|
|
public static void Sort(this List<SbmCostEstimate> values)
|
|
{
|
|
values.Sort
|
|
(
|
|
delegate (SbmCostEstimate costEstimate1, SbmCostEstimate costEstimate2)
|
|
{
|
|
return CompareCatType(values, costEstimate1, costEstimate2);
|
|
}
|
|
);
|
|
}
|
|
|
|
// Costs by category.
|
|
public static bool HasCostsByCategory(this List<SbmCostEstimate> values, Sbm.CostEstimateCategory category)
|
|
{
|
|
if (category < 0) return false;
|
|
return values.Exists(item => item.CostEstimateCategory.Equals(category));
|
|
}
|
|
public static List<String> GetCostCodesByCategory(this List<SbmCostEstimate> values, Sbm.CostEstimateCategory category)
|
|
{
|
|
List<String> codes = new List<String>();
|
|
for (int i = 1; i <= values.Count; i++)
|
|
{
|
|
SbmCostEstimate costEstimateCost = values[i];
|
|
if (costEstimateCost.CostEstimateCategory == category)
|
|
codes.Add(costEstimateCost.Clone().ToString());
|
|
}
|
|
return codes;
|
|
}
|
|
|
|
public static List<SbmCostEstimate> GetCostsByCategory(this List<SbmCostEstimate> values, Sbm.CostEstimateCategory category)
|
|
{
|
|
List<SbmCostEstimate> costEstimateCosts = new List<SbmCostEstimate>();
|
|
for (int i = 1; i <= values.Count; i++)
|
|
{
|
|
SbmCostEstimate costEstimateCost = values[i];
|
|
if (costEstimateCost.CostEstimateCategory == category)
|
|
costEstimateCosts.Add(costEstimateCost.Clone());
|
|
}
|
|
return costEstimateCosts;
|
|
}
|
|
|
|
|
|
// Utilities.
|
|
public static bool HasCostForMinQuantity(this List<SbmCostEstimate> values, ushort quantity, bool ignoreZeroAmount)
|
|
{
|
|
for (int est = 1; est <= values.Count; est++)
|
|
{
|
|
SbmCostEstimate costEstimateCost = values[est];
|
|
if (costEstimateCost.MinQuantity == quantity)
|
|
if (!ignoreZeroAmount || (costEstimateCost.IsLabour() && costEstimateCost.Time > 0.0m) || (!costEstimateCost.IsLabour() && costEstimateCost.Cost > 0.0m)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
//-------------------------------------------------------------------
|
|
// Getting Calculate totals.
|
|
public static decimal GetCostPerUnit(this List<SbmCostEstimate> values, decimal quantity)
|
|
{
|
|
decimal total = 0.0m;
|
|
for (int est = 1; est <= values.Count; est++)
|
|
total += values[est].GetCostPerUnit(quantity);
|
|
return total;
|
|
}
|
|
|
|
public static decimal GetCostPerJob(this List<SbmCostEstimate> values, decimal quantity)
|
|
{
|
|
decimal total = 0.0m;
|
|
for (int est = 1; est <= values.Count; est++)
|
|
total += values[est].GetCostPerJob(quantity);
|
|
return total;
|
|
}
|
|
|
|
public static decimal GetLabourTimePerUnit(this List<SbmCostEstimate> values)
|
|
{
|
|
// Total of times for 'per unit' labour estimates.
|
|
decimal time = 0.0m;
|
|
for (int est = 1; est <= values.Count; est++)
|
|
{
|
|
SbmCostEstimate costEstimateEst = values[est];
|
|
if (costEstimateEst != null && costEstimateEst.IsLabour() && costEstimateEst.IsUnitCost())
|
|
time += costEstimateEst.Time;
|
|
}
|
|
return time;
|
|
}
|
|
|
|
public static decimal GetLabourTimePerJob(this List<SbmCostEstimate> values)
|
|
{
|
|
// Total of times for 'per job' labour estimates.
|
|
decimal time = 0.0m;
|
|
for (int est = 1; est <= values.Count; est++)
|
|
{
|
|
SbmCostEstimate costEstimateEst = values[est];
|
|
if (costEstimateEst != null && costEstimateEst.IsLabour() && costEstimateEst.IsJobCost())
|
|
time += costEstimateEst.Time;
|
|
}
|
|
return time;
|
|
}
|
|
|
|
public static decimal GetLabourTimePerUnit(this List<SbmCostEstimate> values, decimal quantity)
|
|
{
|
|
// Estimated time per unit.
|
|
return (GetLabourTimePerJob(values, quantity) / quantity);
|
|
}
|
|
|
|
public static decimal GetLabourTimePerJob(this List<SbmCostEstimate> values, decimal quantity)
|
|
{
|
|
// Estimated total time for job.
|
|
decimal time = 0.0m;
|
|
for (int est = 1; est <= values.Count; est++)
|
|
{
|
|
SbmCostEstimate costEstimateEst = values[est];
|
|
if (costEstimateEst != null && costEstimateEst.IsLabour())
|
|
{
|
|
if (costEstimateEst.IsUnitCost())
|
|
time += costEstimateEst.Time * quantity;
|
|
else
|
|
time += costEstimateEst.Time;
|
|
}
|
|
}
|
|
return time;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
public static List<SbmCostEstimate> Clone(this List<SbmCostEstimate> values)
|
|
{
|
|
// ToDo CostEstimate list clone: Should each value in the list be cloned?
|
|
return new List<SbmCostEstimate>(values);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
//=======================================================================
|
|
} |