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; } } }