using InABox.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Comal.Classes
{
///
/// The is the way that we are allowing a single to, when received,
/// have its stock distributed to multiple sources. The current idea is this:
///
///
/// -
/// The itself has a and a .
/// In most cases, this will be the target for received stock, and
/// in this simple case, there are no POIAs. If the is blank, it goes to general stock;
/// otherwise, it goes to a specific allocation.
///
/// -
/// If there are allocations, this cuts into the , such that after any allocations have been filled,
/// the remainder of the stock goes to the .
/// If the allocations total more than , then a negative stock movement
/// is registered against .
///
/// -
/// In the case of Dimension conversions (see ), the quantity on the allocation is after the conversion has
/// happened.
/// This is important, because in this case, there will be a discrepancy between and the total
/// of .
///
/// As an example, suppose we are purchasing 2 boxes of 1000 screws. Then, the allocations are by numbers of screws, so that 800 might go to
/// Job A, 600 to Job B and 600 to general stock. So the allocation quantities are the actual numbers that will be on the stock movements.
///
///
///
[Caption("Allocation")]
public class PurchaseOrderItemAllocation : Entity, IRemotable, IPersistent, ILicense
, IOneToMany, IOneToMany, IOneToMany, IPostableFragment
{
[EntityRelationship(DeleteAction.Cascade)]
public PurchaseOrderItemLink Item { get; set; }
[EntityRelationship(DeleteAction.Cascade)]
[EditorSequence(1)]
public JobLink Job { get; set; }
private class JobRequisitionItemLookup : LookupDefinitionGenerator
{
public override Columns DefineFilterColumns()
{
return base.DefineFilterColumns().Add(x => x.Job.ID).Add(x => x.Item.Product.ID);
}
public override Filter? DefineFilter(PurchaseOrderItemAllocation[] items)
{
var jobIDs = items.Select(x => x.Job.ID).Distinct().ToArray();
if(jobIDs.Length > 1)
{
return new Filter().None();
}
var jobID = jobIDs.FirstOrDefault();
var productID = items.Select(x => x.Item.Product.ID).FirstOrDefault();
if(productID == Guid.Empty)
{
return new Filter().None();
}
return new Filter(x => x.Job.ID).IsEqualTo(jobID)
.And(x => x.Product.ID).IsEqualTo(productID);
}
public override Columns DefineColumns()
{
return Columns.None().Add(x => x.Job.JobNumber).Add(x => x.Requisition.Number).Add(x => x.Requisition.Description);
}
public override string FormatDisplay(CoreRow row)
{
var jobNumber = row.Get(x => x.Job.JobNumber);
var requiNumber = row.Get(x => x.Requisition.Number);
var requiDesc = row.Get(x => x.Requisition.Description);
return $"{jobNumber}: #{requiNumber} ({requiDesc})";
}
}
///
/// This may be an empty link. The interface is as such: if there is no JRI, then we are creating a reserve and allocation just against the job. If there is a JRI,
/// then received stock is reserved and allocated for the JRI.
///
[EntityRelationship(DeleteAction.Cascade)]
[LookupDefinition(typeof(JobRequisitionItemLookup))]
[EditorSequence(2)]
public JobRequisitionItemLink JobRequisitionItem { get; set; }
[EditorSequence(3)]
public double Quantity { get; set; }
[NullEditor]
public string PostedReference { get; set; }
protected override void DoPropertyChanged(string name, object? before, object? after)
{
base.DoPropertyChanged(name, before, after);
if(name == $"{nameof(Job)}.{nameof(Job.ID)}")
{
JobRequisitionItem.ID = Guid.Empty;
JobRequisitionItem.Clear();
}
}
}
public class PurchaseOrderItemAllocationJobLink : EntityLink
{
[NullEditor]
public override Guid ID { get; set; }
public LightJobLink Job { get; set; }
public LightPurchaseOrderItemLink Item { get; set; }
}
}