using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using InABox.Clients;
using InABox.Core;
using PRSClasses;
namespace Comal.Classes
{
public enum JobRequisitionItemStatus
{
NotChecked, // Default
///
/// All required stock has been received, and is in the correct .
///
///
/// This can be set even if there are unreceived ,
/// since if we got the stock some other way, we still think of it as allocated.
///
Allocated,
///
/// All required stock has been received, but some is not in the correct , meaning a treatment is required.
///
///
/// This can be set even if there are unreceived ,
/// since if we got the stock some other way, we still think of it as having the stock allocated.
///
TreatmentRequired,
///
/// The has been set, but there are no s for
/// this .
///
OrderRequired,
///
/// We don't yet have all the stock, and there is at least one unreceived of type
/// .
///
OnOrder,
///
/// We don't yet have all the stock, and there is at least one unreceived of type
/// and none of type .
///
TreatmentOnOrder,
[Obsolete]
Received,// Drop
[Obsolete]
TreatmentReceived,// Drop
///
/// The has been cancelled, meaning it has a non-empty .
///
Cancelled,
///
/// The has been archived, meaning it has a non-empty .
///
Archived,
///
/// The has been issued, meaning that it has been allocated, and there are stock movements of type adding up to the correct total.
///
Issued
}
public class JobRequisitionItemTotalQtyFormula : IFormula
{
public Expression> Value => x => x.Qty;
public Expression>[] Modifiers => new Expression>[] { x => x.Dimensions.Value };
public FormulaOperator Operator => FormulaOperator.Multiply;
public FormulaType Type => FormulaType.Virtual;
}
public class JobRequisitionItemInStockAggregate : CoreAggregate
{
public override Expression> Aggregate => x => x.Units;
public override Dictionary>, Expression>> Links => new Dictionary>, Expression>>
{
{ x => x.JobRequisitionItem.ID, x => x.ID }
};
public override AggregateCalculation Calculation => AggregateCalculation.Sum;
}
public class JobRequisitionItemOnOrderAggregate : CoreAggregate
{
public override Expression> Aggregate => x => x.PurchaseOrderItem.Qty;
public override Dictionary>, Expression>> Links => new Dictionary>, Expression>>
{
{ x => x.JobRequisitionItem.ID, x => x.ID }
};
public override AggregateCalculation Calculation => AggregateCalculation.Sum;
public override Filter? Filter =>
new Filter(x => x.PurchaseOrderItem.ReceivedDate).IsEqualTo(null);
}
[Caption("Items")]
[UserTracking(typeof(Job))]
public class JobRequisitionItem : StockEntity, IRemotable, IPersistent, IOneToMany,
ILicense, IJobMaterial, ISequenceable, IIssues
{
[EntityRelationship(DeleteAction.Cascade)]
[Editable(Editable.Hidden)]
public JobLink Job { get; set; }
[EntityRelationship(DeleteAction.Cascade)]
[Editable(Editable.Hidden)]
public JobRequisitionLink Requisition { get; set; }
[EntityRelationship(DeleteAction.SetNull)]
[EditorSequence(1)]
[RequiredColumn]
public override ProductLink Product { get; set; }
[EditorSequence(2)]
[RequiredColumn]
public ProductStyleLink Style { get; set; }
[NullEditor]
[Obsolete("Replaced with Dimensions", true)]
public double UnitSize { get; set; }
[EditorSequence(3)]
[RequiredColumn]
[DimensionsEditor(typeof(StockDimensions), AllowEditingUnit = false)]
public override StockDimensions Dimensions { get; set; }
[EditorSequence(4)]
[RequiredColumn]
public double Qty { get; set; }
[DoubleEditor(Editable = Editable.Hidden)]
[Formula(typeof(JobRequisitionItemTotalQtyFormula))]
public double TotalQty { get; set; }
[EditorSequence(5)]
public double UnitCost { get; set; }
[EditorSequence(6)]
[CurrencyEditor(Summary = Summary.Sum)]
public double TotalCost { get; set; }
[EditorSequence(7)]
[MemoEditor]
public string Notes { get; set; }
[EditorSequence(8)]
public SupplierLink Supplier { get; set; }
[EnumLookupEditor(typeof(JobRequisitionItemStatus), Editable = Editable.Disabled)]
[EditorSequence(9)]
[LoggableProperty]
[RequiredColumn]
public JobRequisitionItemStatus Status { get; set; } = JobRequisitionItemStatus.NotChecked;
///
/// The amount of this requisition item that is currently in stock, which is an aggregate of the property.
///
[Aggregate(typeof(JobRequisitionItemInStockAggregate))]
[DoubleEditor(Editable = Editable.Disabled)]
[EditorSequence(10)]
public double InStock { get; set; }
[Aggregate(typeof(JobRequisitionItemOnOrderAggregate))]
[DoubleEditor(Editable = Editable.Disabled)]
[EditorSequence(11)]
public double OnOrder { get; set; }
[EntityRelationship(DeleteAction.SetNull)]
[RequiredColumn]
[Obsolete("Replaced with JobRequisitionItemPurchaseOrderItem")]
[NullEditor]
public PurchaseOrderItemLink PurchaseOrderItem { get; set; }
[Aggregate(typeof(JobRequisitionItemPurchaseOrderNumberAggregate))]
[TextBoxEditor(Editable = Editable.Hidden)]
public string PurchaseOrderNumbers { get; set; }
[RequiredColumn]
[EditorSequence(12)]
public DateTime Cancelled { get; set; } = DateTime.MinValue;
[RequiredColumn]
[EditorSequence(13)]
public DateTime Archived { get; set; } = DateTime.MinValue;
[RequiredColumn]
[Obsolete("Replaced with JobRequisitionItemPurchaseOrderItem")]
[NullEditor]
public DateTime Ordered { get; set; } = DateTime.MinValue;
[RequiredColumn]
[EditorSequence(14)]
public DateTime OrderRequired { get; set; } = DateTime.MinValue;
[NullEditor]
public string Issues { get; set; }
[NullEditor]
public long Sequence { get; set; }
static JobRequisitionItem()
{
LinkedProperties.Register(x => x.Product.DefaultInstance.Style, x => x.ID, x => x.Style.ID);
LinkedProperties.Register(x => x.Product.DefaultInstance.Style, x => x.Code, x => x.Style.Code);
LinkedProperties.Register(x => x.Product.DefaultInstance.Style, x => x.Description, x => x.Style.Description);
LinkedProperties.Register(x => x.Product.DefaultInstance, x => x.NettCost,
x => x.UnitCost);
StockEntity.LinkStockDimensions();
}
private bool bChanging;
protected override void DoPropertyChanged(string name, object? before, object? after)
{
if (bChanging)
return;
try
{
bChanging = true;
if (name.Equals(nameof(Qty)) && after is double qty)
TotalCost = UnitCost * qty;
else if (name.Equals(nameof(UnitCost)) && after is double cost)
TotalCost = cost * Qty;
else if (name.Equals(nameof(TotalCost)) && after is double total)
{
if (Qty == 0)
Qty = 1;
UnitCost = total / Qty;
}
}
finally
{
bChanging = false;
}
base.DoPropertyChanged(name, before, after);
}
public static void UpdateCosts(IEnumerable items, Dictionary changes)
{
void UpdateValue(JobRequisitionItem item,Expression> property, TType value)
{
CoreUtils.MonitorChanges(
item,
() => CoreUtils.SetPropertyValue(item, CoreUtils.GetFullPropertyName(property, "."), value),
changes
);
}
var productids = items.Where(x => x.Product.ID != Guid.Empty).Select(x => x.Product.ID).ToArray();
MultiQuery query = new MultiQuery();
query.Add(
new Filter(x=>x.Product.ID).InList(productids),
new Columns(x=>x.Product.ID)
.Add(x=>x.SupplierLink.ID)
.Add(x=>x.Style.ID)
.Add(x=>x.Dimensions.UnitSize)
.Add(x=>x.Job.ID)
.Add(x=>x.SupplierCode)
.Add(x=>x.SupplierDescription)
.Add(x=>x.CostPrice)
);
query.Add(
new Filter(x=>x.Product.ID).InList(productids),
new Columns(x=>x.Product.ID)
.Add(x => x.Style.ID)
.Add(x => x.Dimensions.UnitSize)
.Add(x => x.NettCost)
);
query.Query();
foreach (var item in items)
{
//Check Supplier / Job Specific Pricing
CoreRow? row = query.Get()?.Rows.FirstOrDefault(r =>
(r.Get(c => c.Product.ID) == item.Product.ID)
&& (r.Get(c => c.SupplierLink.ID) == item.Supplier.ID)
&& (r.Get(c => c.Style.ID) == item.Style.ID)
&& (string.Equals(r.Get(c=>c.Dimensions.UnitSize),item.Dimensions.UnitSize))
&& (r.Get(c => c.Job.ID) == item.Job.ID)
);
if (row != null)
{
UpdateValue(item, x => x.UnitCost, row.Get(c => c.CostPrice));
continue;
}
// Check Supplier Pricing
row = query.Get()?.Rows.FirstOrDefault(r =>
(r.Get(c => c.Product.ID) == item.Product.ID)
&& (r.Get(c => c.SupplierLink.ID) == item.Supplier.ID)
&& (r.Get(c => c.Style.ID) == item.Style.ID)
&& (string.Equals(r.Get(c=>c.Dimensions.UnitSize),item.Dimensions.UnitSize))
&& (r.Get(c => c.Job.ID) == Guid.Empty)
);
if (row != null)
{
UpdateValue(item, x => x.UnitCost, row.Get(c => c.CostPrice));
continue;
}
// Check Specific Product Instance
row = query.Get()?.Rows.FirstOrDefault(r =>
(r.Get(c => c.Product.ID) == item.Product.ID)
&& (r.Get(c => c.Style.ID) == item.Style.ID)
&& (string.Equals(r.Get(c => c.Dimensions.UnitSize),item.Dimensions.UnitSize))
);
if (row != null)
UpdateValue(item, x => x.UnitCost, row.Get(c => c.NettCost));
else
UpdateValue(item, x => x.UnitCost, 0.0F);
}
}
}
public class JobRequisitionItemPurchaseOrderNumberAggregate : CoreAggregate
{
public override Expression> Aggregate => x => x.PurchaseOrderItem.PurchaseOrderLink.PONumber;
public override Dictionary>, Expression>> Links { get; } = new Dictionary>, Expression>>
{
{ x => x.JobRequisitionItem.ID, x => x.ID }
};
public override AggregateCalculation Calculation => AggregateCalculation.Concat;
}
}