|
@@ -14,26 +14,28 @@ using System.Windows.Controls;
|
|
|
using System.Windows.Forms;
|
|
|
using System.Windows.Media;
|
|
|
|
|
|
-namespace PRSDesktop.Panels.Invoices;
|
|
|
+namespace PRSDesktop;
|
|
|
|
|
|
public class ProgressClaim : BaseObject
|
|
|
{
|
|
|
public JobScopeLink JobScope { get; set; }
|
|
|
|
|
|
- [CurrencyEditor]
|
|
|
+ [CurrencyEditor(Editable = Editable.Disabled, Summary = Summary.Sum)]
|
|
|
public double Materials { get; set; }
|
|
|
|
|
|
- [CurrencyEditor]
|
|
|
+ [CurrencyEditor(Editable = Editable.Disabled, Summary = Summary.Sum)]
|
|
|
public double Labour { get; set; }
|
|
|
|
|
|
+ [DoubleEditor(Editable = Editable.Disabled,Summary = Summary.Sum)]
|
|
|
public double PreviouslyClaimed { get; set; }
|
|
|
|
|
|
+ [DoubleEditor(Editable = Editable.Disabled)]
|
|
|
public double PreviouslyClaimedPercent { get; set; }
|
|
|
|
|
|
- [DoubleEditor]
|
|
|
+ [DoubleEditor(Editable = Editable.Enabled)]
|
|
|
public double PercentCost { get; set; }
|
|
|
|
|
|
- [CurrencyEditor]
|
|
|
+ [DoubleEditor(Editable = Editable.Disabled,Summary = Summary.Sum)]
|
|
|
public double Cost { get; set; }
|
|
|
}
|
|
|
|
|
@@ -42,6 +44,48 @@ public class ProgressClaimGrid : DynamicItemsListGrid<ProgressClaim>
|
|
|
private DynamicGridCustomColumnsComponent<ProgressClaim> ColumnsComponent;
|
|
|
|
|
|
public Guid JobID { get; set; }
|
|
|
+
|
|
|
+ public Guid InvoiceID { get; set; }
|
|
|
+
|
|
|
+ public double ProjectContract { get; private set; }
|
|
|
+ public double ProjectVariations { get; private set; }
|
|
|
+ public double ProjectSubTotal { get; private set; }
|
|
|
+ public double ProjectRetention { get; private set; }
|
|
|
+ public double ProjectRetentionPercent { get; private set; }
|
|
|
+ public double ProjectTotal { get; private set; }
|
|
|
+
|
|
|
+ public double CompletedContract { get; private set; }
|
|
|
+ public double CompletedContractPercent { get; private set; }
|
|
|
+ public double CompletedVariations { get; private set; }
|
|
|
+ public double CompletedVariationsPercent { get; private set; }
|
|
|
+ public double CompletedSubTotal { get; private set; }
|
|
|
+ public double CompletedSubTotalPercent { get; private set; }
|
|
|
+ public double CompletedRetention { get; private set; }
|
|
|
+ public double CompletedRetentionPercent { get; private set; }
|
|
|
+ public double CompletedTotal { get; private set; }
|
|
|
+ public double CompletedTotalPercent { get; private set; }
|
|
|
+
|
|
|
+ public double PreviousContract { get; private set; }
|
|
|
+ public double PreviousContractPercent { get; private set; }
|
|
|
+ public double PreviousVariations { get; private set; }
|
|
|
+ public double PreviousVariationsPercent { get; private set; }
|
|
|
+ public double PreviousSubTotal { get; private set; }
|
|
|
+ public double PreviousSubTotalPercent { get; private set; }
|
|
|
+ public double PreviousRetention { get; private set; }
|
|
|
+ public double PreviousRetentionPercent { get; private set; }
|
|
|
+ public double PreviousTotal { get; private set; }
|
|
|
+ public double PreviousTotalPercent { get; private set; }
|
|
|
+
|
|
|
+ public double CurrentContract { get; private set; }
|
|
|
+ public double CurrentContractPercent { get; private set; }
|
|
|
+ public double CurrentVariations { get; private set; }
|
|
|
+ public double CurrentVariationsPercent { get; private set; }
|
|
|
+ public double CurrentSubTotal { get; private set; }
|
|
|
+ public double CurrentSubTotalPercent { get; private set; }
|
|
|
+ public double CurrentRetention { get; private set; }
|
|
|
+ public double CurrentRetentionPercent { get; private set; }
|
|
|
+ public double CurrentTotal { get; private set; }
|
|
|
+ public double CurrentTotalPercent { get; private set; }
|
|
|
|
|
|
protected override void Init()
|
|
|
{
|
|
@@ -52,10 +96,15 @@ public class ProgressClaimGrid : DynamicItemsListGrid<ProgressClaim>
|
|
|
protected override void DoReconfigure(DynamicGridOptions options)
|
|
|
{
|
|
|
base.DoReconfigure(options);
|
|
|
-
|
|
|
- options.SelectColumns = true;
|
|
|
+ options.SelectColumns = false;
|
|
|
}
|
|
|
|
|
|
+ private Job job;
|
|
|
+ private JobScope[] scopes;
|
|
|
+ private Invoice[] invoices;
|
|
|
+ private Invoice invoice;
|
|
|
+ private List<InvoiceLine> invoicelines;
|
|
|
+
|
|
|
private bool _loadedData = false;
|
|
|
private void LoadData()
|
|
|
{
|
|
@@ -63,10 +112,15 @@ public class ProgressClaimGrid : DynamicItemsListGrid<ProgressClaim>
|
|
|
Columns.None<JobScope>()
|
|
|
.Add(x => x.ID)
|
|
|
.Add(x => x.Number)
|
|
|
- .Add(x => x.ExTax).Add(x => x.InvoiceExTax)
|
|
|
- .Add(x => x.TaxCode.ID).Add(x => x.TaxCode.Rate)
|
|
|
+ .Add(x => x.ExTax)
|
|
|
+ .Add(x => x.InvoiceExTax)
|
|
|
+ .Add(x => x.TaxCode.ID)
|
|
|
+ .Add(x => x.TaxCode.Rate)
|
|
|
+ .Add(x=>x.Tax)
|
|
|
.Add(x => x.Description)
|
|
|
- .Add(x => x.UninvoicedMaterialsExTax);
|
|
|
+ .Add(x => x.UninvoicedMaterialsExTax)
|
|
|
+ .Add(x => x.Job.DefaultScope.ID)
|
|
|
+ .Add(x=>x.Type);
|
|
|
|
|
|
var scopeColumn = new Column<ProgressClaim>(x => x.JobScope).Property + ".";
|
|
|
foreach(var column in DataColumns().Where(x => x.Property.StartsWith(scopeColumn)))
|
|
@@ -74,40 +128,189 @@ public class ProgressClaimGrid : DynamicItemsListGrid<ProgressClaim>
|
|
|
columns.Add(column.Property[scopeColumn.Length..]);
|
|
|
}
|
|
|
|
|
|
- var scopes = Client.Query<JobScope>(
|
|
|
+ MultiQuery query = new MultiQuery();
|
|
|
+
|
|
|
+ query.Add(
|
|
|
+ new Filter<Job>(x=>x.ID).IsEqualTo(JobID),
|
|
|
+ Columns.None<Job>()
|
|
|
+ .Add(x=>x.Retention.Maximum)
|
|
|
+ .Add(x=>x.Retention.Rate)
|
|
|
+ .Add(x=>x.Retention.IncludeVariations)
|
|
|
+ );
|
|
|
+
|
|
|
+ query.Add(
|
|
|
new Filter<JobScope>(x => x.Job.ID).IsEqualTo(JobID)
|
|
|
.And(x => x.Status.Approved).IsEqualTo(true),
|
|
|
- columns)
|
|
|
- .ToArray<JobScope>();
|
|
|
-
|
|
|
- var assignments = Client.Query(
|
|
|
- new Filter<Assignment>(x => x.JobScope.ID).InList(scopes.ToArray(x => x.ID))
|
|
|
+ columns);
|
|
|
+
|
|
|
+ query.Add(
|
|
|
+ new Filter<Assignment>(x => x.JobLink.ID).IsEqualTo(JobID)
|
|
|
.And(x => x.Invoice.ID).IsEqualTo(Guid.Empty),
|
|
|
Columns.None<Assignment>().Add(x => x.JobScope.ID)
|
|
|
.Add(x => x.Actual.Duration)
|
|
|
- .Add(x => x.EmployeeLink.HourlyRate))
|
|
|
- .ToObjects<Assignment>()
|
|
|
+ .Add(x => x.EmployeeLink.HourlyRate));
|
|
|
+
|
|
|
+ query.Add(new Filter<Invoice>(x=>x.JobLink.ID).IsEqualTo(JobID));
|
|
|
+
|
|
|
+ if (InvoiceID != Guid.Empty)
|
|
|
+ query.Add(new Filter<InvoiceLine>(x=>x.InvoiceLink.ID).IsEqualTo(InvoiceID));
|
|
|
+
|
|
|
+ query.Query();
|
|
|
+
|
|
|
+ job = query.Get<Job>().ToObjects<Job>().FirstOrDefault() ?? new Job();
|
|
|
+
|
|
|
+ scopes = query.Get<JobScope>().ToArray<JobScope>();
|
|
|
+
|
|
|
+ var assignments = query.Get<Assignment>().ToObjects<Assignment>()
|
|
|
.GroupBy(x => x.JobScope.ID)
|
|
|
.ToDictionary(x => x.Key, x => x.Sum(x => x.Actual.Duration.TotalHours * x.EmployeeLink.HourlyRate));
|
|
|
|
|
|
+ invoices = query.Get<Invoice>().ToArray<Invoice>();
|
|
|
+
|
|
|
+ invoice = invoices.FirstOrDefault(x => x.ID == InvoiceID) ?? new Invoice();
|
|
|
+
|
|
|
+ invoicelines = InvoiceID != Guid.Empty
|
|
|
+ ? query.Get<InvoiceLine>().ToObjects<InvoiceLine>().ToList()
|
|
|
+ : new List<InvoiceLine>();
|
|
|
+
|
|
|
+
|
|
|
var items = new List<ProgressClaim>();
|
|
|
foreach(var scope in scopes)
|
|
|
{
|
|
|
var newItem = new ProgressClaim();
|
|
|
newItem.JobScope.CopyFrom(scope);
|
|
|
- newItem.PreviouslyClaimed = scope.InvoiceExTax;
|
|
|
- newItem.PreviouslyClaimedPercent = scope.ExTax.IsEffectivelyEqual(0.0) ? 0.0 : scope.InvoiceExTax / scope.ExTax * 100;
|
|
|
- newItem.PercentCost = newItem.PreviouslyClaimedPercent;
|
|
|
- newItem.Cost = 0;
|
|
|
+
|
|
|
+ var invoiceline = invoicelines.FirstOrDefault(x =>
|
|
|
+ x.Scope.ID == scope.ID || (x.Scope.ID == Guid.Empty && scope.ID == scope.Job.DefaultScope.ID));
|
|
|
+ if (invoiceline != null)
|
|
|
+ {
|
|
|
+ newItem.PreviouslyClaimed = scope.InvoiceExTax - invoiceline.ExTax;
|
|
|
+ newItem.PreviouslyClaimedPercent = scope.ExTax.IsEffectivelyEqual(0.0) ? 0.0 : (scope.InvoiceExTax - invoiceline.ExTax) / scope.ExTax * 100;
|
|
|
+ newItem.PercentCost = scope.ExTax.IsEffectivelyEqual(0.0) ? 0.0 : scope.InvoiceExTax / scope.ExTax * 100;
|
|
|
+ newItem.Cost = invoiceline.ExTax;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ newItem.PreviouslyClaimed = scope.InvoiceExTax;
|
|
|
+ newItem.PreviouslyClaimedPercent = scope.ExTax.IsEffectivelyEqual(0.0) ? 0.0 : scope.InvoiceExTax / scope.ExTax * 100;
|
|
|
+ newItem.PercentCost = newItem.PreviouslyClaimedPercent;
|
|
|
+ newItem.Cost = 0;
|
|
|
+ }
|
|
|
newItem.Materials = scope.UninvoicedMaterialsExTax;
|
|
|
newItem.Labour = assignments.GetValueOrDefault(scope.ID);
|
|
|
+
|
|
|
items.Add(newItem);
|
|
|
}
|
|
|
Items = items;
|
|
|
|
|
|
+ CalculateTotals();
|
|
|
+
|
|
|
_loadedData = true;
|
|
|
}
|
|
|
|
|
|
+ protected override void Changed()
|
|
|
+ {
|
|
|
+ base.Changed();
|
|
|
+ CalculateTotals();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void CalculateTotals()
|
|
|
+ {
|
|
|
+ Guid[] contractscopeids = scopes.Where(x => x.Type == JobScopeType.Contract).Select(x => x.ID).ToArray();
|
|
|
+ Guid[] variationscopeids = scopes.Where(x => x.Type != JobScopeType.Contract).Select(x => x.ID).ToArray();
|
|
|
+
|
|
|
+ ProjectContract = Items.Where(x => contractscopeids.Contains(x.JobScope.ID)).Sum(x => x.JobScope.ExTax);
|
|
|
+
|
|
|
+ PreviousContract = Items.Where(x => contractscopeids.Contains(x.JobScope.ID)).Sum(x => x.PreviouslyClaimed);
|
|
|
+ PreviousContractPercent = ProjectContract.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : PreviousContract * 100.0 / ProjectContract;
|
|
|
+
|
|
|
+ CurrentContract = Items.Where(x => contractscopeids.Contains(x.JobScope.ID)).Sum(x => x.Cost);
|
|
|
+ CurrentContractPercent = ProjectContract.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : CurrentContract * 100.0 / ProjectContract;
|
|
|
+
|
|
|
+ CompletedContract = PreviousContract + CurrentContract;
|
|
|
+ CompletedContractPercent = ProjectContract.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : CompletedContract * 100.0 / ProjectContract;
|
|
|
+
|
|
|
+ ProjectVariations = Items.Where(x => variationscopeids.Contains(x.JobScope.ID)).Sum(x => x.JobScope.ExTax);
|
|
|
+
|
|
|
+ PreviousVariations = Items.Where(x => variationscopeids.Contains(x.JobScope.ID)).Sum(x => x.PreviouslyClaimed);
|
|
|
+ PreviousVariationsPercent = ProjectVariations.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : PreviousVariations * 100.0 / ProjectVariations;
|
|
|
+
|
|
|
+ CurrentVariations = Items.Where(x => variationscopeids.Contains(x.JobScope.ID)).Sum(x => x.Cost);
|
|
|
+ CurrentVariationsPercent = ProjectVariations.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : CurrentVariations * 100.0 / ProjectVariations;
|
|
|
+
|
|
|
+ CompletedVariations = PreviousVariations + CurrentVariations;
|
|
|
+ CompletedVariationsPercent = ProjectVariations.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : CompletedVariations * 100.0 / ProjectVariations;
|
|
|
+
|
|
|
+
|
|
|
+ ProjectSubTotal = ProjectContract + ProjectVariations;
|
|
|
+
|
|
|
+ PreviousSubTotal = PreviousContract + PreviousVariations;
|
|
|
+ PreviousSubTotalPercent = ProjectSubTotal.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : PreviousSubTotal * 100.0 / ProjectSubTotal;
|
|
|
+
|
|
|
+ CurrentSubTotal = CurrentContract + CurrentVariations;
|
|
|
+ CurrentSubTotalPercent = ProjectSubTotal.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : CurrentSubTotal * 100.0 / ProjectSubTotal;
|
|
|
+
|
|
|
+ CompletedSubTotal = PreviousSubTotal + CurrentSubTotal;
|
|
|
+ CompletedSubTotalPercent = ProjectSubTotal.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : CompletedSubTotal * 100.0 / ProjectSubTotal;
|
|
|
+
|
|
|
+ var retentionbase = job.Retention.IncludeVariations
|
|
|
+ ? ProjectSubTotal
|
|
|
+ : ProjectContract;
|
|
|
+ ProjectRetention = retentionbase * job.Retention.Maximum / 100.0;
|
|
|
+ ProjectRetentionPercent = job.Retention.Maximum;
|
|
|
+
|
|
|
+
|
|
|
+ PreviousRetention = invoices.Where(x => x.ID != InvoiceID).Sum(x=>x.Retained);
|
|
|
+ PreviousRetentionPercent = retentionbase.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : PreviousRetention * 100.0 / retentionbase;
|
|
|
+
|
|
|
+ CurrentRetention = Math.Min(ProjectRetention - PreviousRetention,CurrentSubTotal * job.Retention.Rate / 100.0);
|
|
|
+ CurrentRetentionPercent = retentionbase.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : CurrentRetention * 100.0 / retentionbase;
|
|
|
+
|
|
|
+ CompletedRetention = PreviousRetention + CurrentRetention;
|
|
|
+ CompletedRetentionPercent = retentionbase.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : CompletedRetention * 100.0 / retentionbase;
|
|
|
+
|
|
|
+ ProjectTotal = (ProjectSubTotal - ProjectRetention);
|
|
|
+
|
|
|
+ CompletedTotal = (CompletedSubTotal - CompletedRetention);
|
|
|
+ CompletedTotalPercent = ProjectTotal.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : CompletedTotal * 100.0 / ProjectTotal;
|
|
|
+
|
|
|
+ PreviousTotal = (PreviousSubTotal - PreviousRetention);
|
|
|
+ PreviousTotalPercent = ProjectTotal.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : PreviousTotal * 100.0 / ProjectTotal;
|
|
|
+
|
|
|
+ CurrentTotal = (CurrentSubTotal - CurrentRetention);
|
|
|
+ CurrentTotalPercent = ProjectTotal.IsEffectivelyEqual(0.0)
|
|
|
+ ? 0.0
|
|
|
+ : CurrentTotal * 100.0 / ProjectTotal;
|
|
|
+ }
|
|
|
+
|
|
|
protected override void Reload(Filters<ProgressClaim> criteria, Columns<ProgressClaim> columns, ref SortOrder<ProgressClaim>? sort, CancellationToken token, Action<CoreTable?, Exception?> action)
|
|
|
{
|
|
|
LoadData();
|
|
@@ -144,7 +347,7 @@ public class ProgressClaimGrid : DynamicItemsListGrid<ProgressClaim>
|
|
|
var columns = ColumnsComponent.LoadColumns();
|
|
|
|
|
|
ActionColumns.Clear();
|
|
|
-
|
|
|
+
|
|
|
ActionColumns.Add(new DynamicTemplateColumn(row =>
|
|
|
{
|
|
|
var item = LoadItem(row);
|
|
@@ -176,17 +379,18 @@ public class ProgressClaimGrid : DynamicItemsListGrid<ProgressClaim>
|
|
|
DoChanged();
|
|
|
}
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
return editor;
|
|
|
})
|
|
|
{
|
|
|
HeaderText = "Cur %",
|
|
|
- Width = 80
|
|
|
+ Width = 80,
|
|
|
});
|
|
|
+
|
|
|
ActionColumns.Add(new DynamicTextColumn(row =>
|
|
|
{
|
|
|
if (row is null) return null;
|
|
|
-
|
|
|
+
|
|
|
var item = LoadItem(row);
|
|
|
return item.Cost;
|
|
|
})
|
|
@@ -198,4 +402,35 @@ public class ProgressClaimGrid : DynamicItemsListGrid<ProgressClaim>
|
|
|
|
|
|
return columns;
|
|
|
}
|
|
|
+
|
|
|
+ private DynamicGridTreeUIComponent<ProgressClaim>? _uiComponent;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ private DynamicGridTreeUIComponent<ProgressClaim> UIComponent
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ if(_uiComponent is null)
|
|
|
+ {
|
|
|
+ _uiComponent = new DynamicGridTreeUIComponent<ProgressClaim>(
|
|
|
+ x => x.JobScope.ID,
|
|
|
+ x => x.JobScope.Parent.ID)
|
|
|
+ {
|
|
|
+ Parent = this,
|
|
|
+ MaxRowHeight = 30,
|
|
|
+ };
|
|
|
+
|
|
|
+ //_uiComponent.OnContextMenuOpening += JobDocumentSetFolderTree_OnContextMenuOpening;
|
|
|
+ }
|
|
|
+ return _uiComponent;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override IDynamicGridUIComponent<ProgressClaim> CreateUIComponent()
|
|
|
+ {
|
|
|
+ return UIComponent;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
}
|