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 .
/// Allocated + Issued >= Qty
///
///
/// 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.
/// InStock + Issued >= Qty
///
///
/// 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 .
/// Issued >= Qty
///
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 interface IJobRequisitionItem : IEntity
{
public DateTime Cancelled { get; set; }
}
[Caption("Items")]
[UserTracking(typeof(Job))]
public class JobRequisitionItem : StockEntity, IRemotable, IPersistent, IOneToMany,
ILicense, IJobMaterial, ISequenceable, IIssues, IJobRequisitionItem, IProblems
{
[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))]
public override StockDimensions Dimensions { get; set; }
[EditorSequence(4)]
[RequiredColumn]
public double Qty { get; set; }
private class TotalQtyFormula : ComplexFormulaGenerator
{
public override IComplexFormulaNode GetFormula() =>
Formula(FormulaOperator.Multiply, Property(x => x.Qty), Property(x => x.Dimensions.Value));
}
[DoubleEditor(Editable = Editable.Hidden)]
[ComplexFormula(typeof(TotalQtyFormula))]
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; }
[NullEditor]
[EditorSequence(9)]
[LoggableProperty]
[RequiredColumn]
[Obsolete("", true)]
public JobRequisitionItemStatus Status { get; set; } = JobRequisitionItemStatus.NotChecked;
private class InStockFormula : ComplexFormulaGenerator
{
public override IComplexFormulaNode GetFormula() =>
Aggregate(AggregateCalculation.Sum, x => x.Property(x => x.Units))
.WithLink(x => x.JobRequisitionItem.ID, x => x.ID);
}
[ComplexFormula(typeof(InStockFormula))]
[DoubleEditor(Editable = Editable.Disabled)]
[EditorSequence(10)]
///
/// The amount of this requisition item that is currently in stock, which is an aggregate of the property.
///
public double InStock { get; set; }
private class TotalOrdersFormula : ComplexFormulaGenerator
{
public override IComplexFormulaNode GetFormula() =>
Aggregate(
AggregateCalculation.Sum,
x => x.Property(x => x.Quantity),
new Filter(x => x.Item.ReceivedDate).IsEqualTo(DateTime.MinValue))
.WithLink(x => x.JobRequisitionItem.ID, x => x.ID);
}
[ComplexFormula(typeof(TotalOrdersFormula))]
[DoubleEditor(Editable = Editable.Disabled, Visible = Visible.Optional)]
[EditorSequence(11)]
public double TotalOrders { get; set; }
private class OnOrderFormula : ComplexFormulaGenerator
{
public override IComplexFormulaNode GetFormula() =>
Aggregate(
AggregateCalculation.Sum,
x => x.Property(x => x.Quantity),
new Filter(x => x.Item.ReceivedDate).IsEqualTo(null))
.WithLink(x => x.JobRequisitionItem.ID, x => x.ID)
.WithLink(x => x.Item.Product.ID, x => x.Product.ID);
}
[ComplexFormula(typeof(OnOrderFormula))]
[DoubleEditor(Editable = Editable.Disabled)]
[EditorSequence(12)]
public double OnOrder { get; set; }
private class TreatmentRequiredFormula : ComplexFormulaGenerator
{
public override IComplexFormulaNode GetFormula() =>
Formula(FormulaOperator.Subtract, Property(x => x.InStock), Property(x => x.Allocated));
}
[ComplexFormula(typeof(TreatmentRequiredFormula))]
[DoubleEditor(Editable = Editable.Disabled)]
[EditorSequence(13)]
public double TreatmentRequired { get; set; }
private class TreatmentOnOrderFormula : ComplexFormulaGenerator
{
public override IComplexFormulaNode GetFormula() =>
Formula(FormulaOperator.Subtract, Property(x => x.TotalOrders), Property(x => x.OnOrder));
}
[ComplexFormula(typeof(TreatmentOnOrderFormula))]
[DoubleEditor(Editable = Editable.Disabled)]
[EditorSequence(14)]
public double TreatmentOnOrder { get; set; }
private class AllocatedFormula : ComplexFormulaGenerator
{
public override IComplexFormulaNode GetFormula() =>
Aggregate(
AggregateCalculation.Sum,
x => x.Property(x => x.Units))
.WithLink(x => x.JobRequisitionItem.ID, x => x.ID)
.WithLink(x => x.Style.ID, x => x.Style.ID);
}
[ComplexFormula(typeof(AllocatedFormula))]
[DoubleEditor(Editable = Editable.Disabled)]
[EditorSequence(15)]
public double Allocated { get; set; }
private class PickRequestedFormula : ComplexFormulaGenerator
{
public override IComplexFormulaNode GetFormula() =>
Aggregate(AggregateCalculation.Sum, x => x.Property(x => x.Quantity), new Filter(x => x.RequisitionLink.StockUpdated).IsEqualTo(DateTime.MinValue))
.WithLink(x => x.SourceJRI.ID, x => x.ID);
}
[ComplexFormula(typeof(PickRequestedFormula))]
[DoubleEditor(Editable = Editable.Disabled)]
[EditorSequence(16)]
public double PickRequested { get; set; }
private class IssuedFormula : ComplexFormulaGenerator
{
public override IComplexFormulaNode GetFormula() =>
Aggregate(
AggregateCalculation.Sum,
x => x.Property(x => x.Issued),
new Filter(x => x.Type).IsEqualTo(StockMovementType.Issue))
.WithLink(x => x.JobRequisitionItem.ID, x => x.ID)
.WithLink(x => x.Style.ID, x => x.Style.ID);
}
[ComplexFormula(typeof(IssuedFormula))]
[DoubleEditor(Editable = Editable.Disabled)]
[EditorSequence(17)]
public double Issued { get; set; }
[EntityRelationship(DeleteAction.SetNull)]
[RequiredColumn]
[Obsolete("Replaced with JobRequisitionItemPurchaseOrderItem")]
[NullEditor]
public PurchaseOrderItemLink PurchaseOrderItem { get; set; }
private class PurchaseOrderNumbersFormula : ComplexFormulaGenerator
{
public override IComplexFormulaNode GetFormula() =>
Aggregate(
AggregateCalculation.Concat,
x => x.Property(x => x.Item.PurchaseOrderLink.PONumber))
.WithLink(x => x.JobRequisitionItem.ID, x => x.ID);
}
[ComplexFormula(typeof(PurchaseOrderNumbersFormula))]
[TextBoxEditor(Editable = Editable.Hidden)]
public string PurchaseOrderNumbers { get; set; }
[RequiredColumn]
[EditorSequence(18)]
public DateTime Cancelled { get; set; } = DateTime.MinValue;
[RequiredColumn]
[EditorSequence(19)]
public DateTime Archived { get; set; } = DateTime.MinValue;
[RequiredColumn]
[Obsolete("Replaced with JobRequisitionItemPurchaseOrderItem")]
[NullEditor]
public DateTime Ordered { get; set; } = DateTime.MinValue;
[RequiredColumn]
[EditorSequence(20)]
public DateTime OrderRequired { get; set; } = DateTime.MinValue;
[NullEditor]
[Obsolete("Replaced with Problem", true)]
public string Issues { get; set; }
[EditorSequence("Issues", 1)]
public ManagedProblem Problem { 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),
Columns.None().Add(x=>x.Product.ID)
.Add(x=>x.SupplierLink.ID)
.Add(x=>x.Style.ID)
.AddDimensionsColumns(x => x.Dimensions, Classes.Dimensions.ColumnsType.Local)
.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),
Columns.None().Add(x=>x.Product.ID)
.Add(x => x.Style.ID)
.AddDimensionsColumns(x => x.Dimensions, Classes.Dimensions.ColumnsType.Local)
.Add(x => x.NettCost)
);
query.Query();
var supplierProducts = query.Get().ToArray();
var productInstances = query.Get().ToArray();
foreach (var item in items)
{
//Check Supplier / Job Specific Pricing
var supplierProduct = supplierProducts.FirstOrDefault(x =>
x.Product.ID == item.Product.ID
&& x.SupplierLink.ID == item.Supplier.ID
&& x.Style.ID == item.Style.ID
&& x.Dimensions.Equals(item.Dimensions)
&& x.Job.ID == item.Job.ID);
if (supplierProduct != null)
{
UpdateValue(item, x => x.UnitCost, supplierProduct.CostPrice);
continue;
}
// Check Supplier Pricing
supplierProduct = supplierProducts.FirstOrDefault(x =>
x.Product.ID == item.Product.ID
&& x.SupplierLink.ID == item.Supplier.ID
&& x.Style.ID == item.Style.ID
&& x.Dimensions.Equals(item.Dimensions)
&& x.Job.ID == Guid.Empty);
if (supplierProduct != null)
{
UpdateValue(item, x => x.UnitCost, supplierProduct.CostPrice);
continue;
}
// Check Specific Product Instance
var productInstance = productInstances.FirstOrDefault(x =>
x.Product.ID == item.Product.ID
&& x.Style.ID == item.Style.ID
&& x.Dimensions.Equals(item.Dimensions));
if (productInstance != null)
UpdateValue(item, x => x.UnitCost, productInstance.NettCost);
else
UpdateValue(item, x => x.UnitCost, 0.0F);
}
}
}
}