| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
- using InABox.Core;
- using PRSClasses;
- namespace Comal.Classes
- {
- public class StockMovementLink : EntityLink<StockMovement>
- {
- [NullEditor]
- public override Guid ID { get; set; }
- }
- [UserTracking("Warehousing")]
- public class StockMovement : StockEntity, IRemotable, IPersistent, IOneToMany<StockLocation>, IOneToMany<Product>,
- ILicense<WarehouseLicense>, IStockHolding, IJobMaterial, IExportable, IImportable, IPostable
- {
- [DateTimeEditor]
- [EditorSequence(0)]
- [SecondaryIndex]
- public DateTime Date { get; set; }
- private class ProductLookupGenerator : LookupDefinitionGenerator<Product, StockMovement>
- {
- public override Filter<Product>? DefineFilter(StockMovement[] items)
- => LookupFactory.DefineFilter<Product>().And(x => x.NonStock).IsEqualTo(false);
- }
- [EditorSequence(1)]
- [EntityRelationship(DeleteAction.Cascade)]
- [RequiredColumn]
- [LookupDefinition(typeof(ProductLookupGenerator))]
- public override ProductLink Product { get; set; }
-
-
- [EditorSequence(2)]
- [RequiredColumn]
- [DimensionsEditor(typeof(StockDimensions))]
- public override StockDimensions Dimensions { get; set; }
- [EditorSequence(3)]
- [EntityRelationship(DeleteAction.SetNull)]
- public ProductStyleLink Style { get; set; }
-
- // Allowed to be negative.
- [DoubleEditor(Summary = Summary.Sum)]
- [EditorSequence(4)]
- public double Received { get; set; }
- [DoubleEditor(Summary = Summary.Sum)]
- [EditorSequence(5)]
- public double Issued { get; set; }
- /// <summary>
- /// This indicates the balance of the holding at the current point in time for a stock movement, populated for stock takes.
- /// </summary>
- [DoubleEditor]
- [EditorSequence(6)]
- public double Balance { get; set; }
- private class StockMovementUnitsFormula : ComplexFormulaGenerator<StockMovement, double>
- {
- public override IComplexFormulaNode<StockMovement, double> GetFormula() =>
- Formula(FormulaOperator.Subtract, Property(x => x.Received), Property(x => x.Issued));
- }
- /// <summary>
- /// Units = Received - Issued
- /// </summary>
- [ComplexFormula(typeof(StockMovementUnitsFormula))]
- [EditorSequence(7)]
- [DoubleEditor(Visible=Visible.Optional, Editable = Editable.Hidden, Summary= Summary.Sum)]
- public double Units { get; set; }
- private class IsRemnantCondition : ComplexFormulaGenerator<StockMovement, bool>
- {
- public override IComplexFormulaNode<StockMovement, bool> GetFormula() =>
- If<double>(
- x => x.Property(x => x.Dimensions.Value),
- Condition.LessThan,
- x => x.Property(x => x.Product.DefaultInstance.Dimensions.Value))
- .Then(Constant(true))
- .Else(Constant(false));
- }
- /// <summary>
- /// IsRemnant = Dimensions.Value < Product.Dimensions.Value
- /// </summary>
- [CheckBoxEditor(Editable = Editable.Hidden)]
- [ComplexFormula(typeof(IsRemnantCondition))]
- [EditorSequence(7)]
- public bool IsRemnant { get; set; }
-
- private class QuantityFormula : ComplexFormulaGenerator<StockMovement, double>
- {
- public override IComplexFormulaNode<StockMovement, double> GetFormula() =>
- Formula(FormulaOperator.Multiply,
- Property(x => x.Units),
- Property(x => x.Dimensions.Value));
- }
- /// <summary>
- /// Qty = Units * Dimensions.Value
- /// </summary>
- [EditorSequence(8)]
- [ComplexFormula(typeof(QuantityFormula))]
- [DoubleEditor(Editable = Editable.Hidden, Summary = Summary.Sum)]
- public double Qty { get; set; }
-
-
- [CurrencyEditor(Visible = Visible.Default)]
- [EditorSequence(9)]
- public double Cost { get; set; } = 0.0;
-
- [EditorSequence(10)]
- [EntityRelationship(DeleteAction.SetNull)]
- public StockLocationLink Location { get; set; }
-
- [EditorSequence(11)]
- [EntityRelationship(DeleteAction.SetNull)]
- public JobLink Job { get; set; }
-
- [MemoEditor]
- [EditorSequence(12)]
- public string Notes { get; set; }
- [EditorSequence(13)]
- [EnumLookupEditor(typeof(StockMovementType), Visible = Visible.Default)]
- public StockMovementType Type { get; set; }
- [EditorSequence(14)]
- [EntityRelationship(DeleteAction.SetNull)]
- public EmployeeLink Employee { get; set; }
-
- /// <summary>
- /// To link StockMovements into conceptual blocks that cannot exist independently of each other; for example,
- /// an issue may require transfers in and out which are intrinsically tied to the issue.
- /// </summary>
- /// <remarks>
- /// <b>Important:</b> if this stock movement is a transfer, use <see cref="StockMovement.LinkTransfers(StockMovement, StockMovement, Guid?)"/>.
- /// </remarks>
- [NullEditor]
- public Guid Transaction { get; set; } = Guid.NewGuid();
- /// <summary>
- /// To link TransferOut/TransferIn together pairwise. <b>Only</b> edit via the Link* methods provided by <see cref="StockMovement"/>, such
- /// as <see cref="StockMovement.LinkTransfers(StockMovement, StockMovement, Guid?)"/>.
- /// </summary>
- [NullEditor]
- public Guid TransferID { get; set; } = Guid.NewGuid();
- [NullEditor]
- public bool System { get; set; }
- [NullEditor]
- [Obsolete("Replaced with Type", true)]
- public bool IsTransfer { get; set; } = false;
- [NullEditor]
- public PurchaseOrderItemLink OrderItem { get; set; }
- private class JobRequisitionItemLookup : LookupDefinitionGenerator<JobRequisitionItem, StockMovement>
- {
- public override Columns<JobRequisitionItem> DefineColumns()
- {
- return Columns.None<JobRequisitionItem>().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<JobRequisitionItem, string>(x => x.Job.JobNumber);
- var requiNumber = row.Get<JobRequisitionItem, int>(x => x.Requisition.Number);
- var requiDesc = row.Get<JobRequisitionItem, string>(x => x.Requisition.Description);
- return $"{jobNumber}: #{requiNumber} ({requiDesc})";
- }
- }
- [RequiredColumn]
- [LookupDefinition(typeof(JobRequisitionItemLookup))]
- public JobRequisitionItemLink JobRequisitionItem { get; set; }
- [NullEditor]
- public InvoiceLink Invoice { get; set; }
- private class JobScopeLookup : LookupDefinitionGenerator<JobScope, StockMovement>
- {
- public override Filter<JobScope> DefineFilter(StockMovement[] items)
- {
- var item = items?.Length == 1 ? items[0] : null;
- if (item != null)
- return Filter<JobScope>.Where(x => x.Job.ID).IsEqualTo(item.Job.ID).And(x => x.Status.Approved).IsEqualTo(true);
- return Filter.None<JobScope>();
- }
-
- public override Columns<StockMovement> DefineFilterColumns()
- => Columns.None<StockMovement>().Add(x=>x.Job.ID);
- }
- [LookupDefinition(typeof(JobScopeLookup))]
- [EditorSequence(5)]
- [EntityRelationship(DeleteAction.SetNull)]
- public JobScopeLink JobScope { get; set; }
- public ActualCharge Charge { get; set; }
- private class DocumentsCount : ComplexFormulaGenerator<StockMovement, int>
- {
- public override IComplexFormulaNode<StockMovement, int> GetFormula() =>
- Count<StockMovementBatchDocument, Guid>(
- x => x.Property(x => x.ID))
- .WithLink(x => x.EntityLink.ID, x => x.Batch.ID);
- }
- [ComplexFormula(typeof(DocumentsCount))]
- [NullEditor]
- public int Documents { get; set; }
- /// <summary>
- /// Used to Group together movements (particularly images)
- /// when transactions are uploaded from Mobile Devices
- /// </summary>
- [EntityRelationship(DeleteAction.Cascade)]
- public StockMovementBatchLink Batch { get; set; }
-
- [NullEditor]
- [Obsolete("Replaced with Dimensions", true)]
- public double UnitSize { get; set; }
-
- private class ValueFormula : ComplexFormulaGenerator<StockMovement, double>
- {
- public override IComplexFormulaNode<StockMovement, double> GetFormula() =>
- Formula(FormulaOperator.Multiply,
- Property(x => x.Units),
- Property(x => x.Cost));
- }
- /// <summary>
- /// Value of a stock movement, equal to <see cref="Units"/> * <see cref="Cost"/>.
- /// </summary>
- [CurrencyEditor(Visible = Visible.Optional, Editable = Editable.Hidden, Summary=Summary.Sum)]
- [ComplexFormula(typeof(ValueFormula))]
- public double Value { get; set; } = 0.0;
- [NullEditor]
- [LoggableProperty]
- public DateTime Posted { get; set; }
- [NullEditor]
- [LoggableProperty]
- [RequiredColumn]
- public PostedStatus PostedStatus { get; set; }
- [NullEditor]
- public string PostedNote { get; set; }
- [NullEditor]
- public string PostedReference { get; set; }
- /// <summary>
- /// Link this stock movement to <paramref name="other"/>, which must be a <see cref="StockMovementType.TransferIn"/>. This
- /// will also set this movement to be <see cref="StockMovementType.TransferOut"/>.
- /// </summary>
- /// <remarks>
- /// Links both <see cref="Transaction"/> and <see cref="TransferID"/>; if <paramref name="transaction"/> is provided, then
- /// uses that instead of <paramref name="other"/>.Transaction.
- /// </remarks>
- public void LinkAsTransferOut(StockMovement other, Guid? transaction = null)
- {
- if(other.Type != StockMovementType.TransferIn)
- {
- throw new ArgumentException($"Provided stock movement is not a {nameof(StockMovementType.TransferIn)}", nameof(other));
- }
- else if(other.TransferID != Guid.Empty)
- {
- throw new ArgumentException($"Provided stock movement has already been linked to a transfer", nameof(other));
- }
- Transaction = transaction ?? other.Transaction;
- var transfer = Guid.NewGuid();
- other.TransferID = transfer;
- TransferID = transfer;
- Type = StockMovementType.TransferOut;
- }
- /// <summary>
- /// Link this stock movement to <paramref name="other"/>, which must be a <see cref="StockMovementType.TransferOut"/>. This
- /// will also set this movement to be <see cref="StockMovementType.TransferIn"/>.
- /// </summary>
- /// <remarks>
- /// Links both <see cref="Transaction"/> and <see cref="TransferID"/>; if <paramref name="transaction"/> is provided, then
- /// uses that instead of <paramref name="other"/>.Transaction.
- /// </remarks>
- public void LinkAsTransferIn(StockMovement other, Guid? transaction = null)
- {
- if(other.Type != StockMovementType.TransferOut)
- {
- throw new ArgumentException($"Provided stock movement is not a {nameof(StockMovementType.TransferOut)}", nameof(other));
- }
- else if(other.TransferID != Guid.Empty)
- {
- throw new ArgumentException($"Provided stock movement has already been linked to a transfer", nameof(other));
- }
- Transaction = transaction ?? other.Transaction;
- var transfer = Guid.NewGuid();
- other.TransferID = transfer;
- TransferID = transfer;
- Type = StockMovementType.TransferIn;
- }
- /// <summary>
- /// Link the provided transfers together, and set them to have type <see cref="StockMovementType.TransferOut"/> and
- /// <see cref="StockMovementType.TransferIn"/>, respectively.
- /// </summary>
- /// <exception cref="ArgumentException">If either transfer has already been linked to a transfer.</exception>
- public static void LinkTransfers(StockMovement transferOut, StockMovement transferIn, Guid? transaction = null)
- {
- if(transferOut.TransferID != Guid.Empty)
- {
- throw new ArgumentException($"Provided stock movement has already been linked to a transfer", nameof(transferOut));
- }
- else if(transferIn.TransferID != Guid.Empty)
- {
- throw new ArgumentException($"Provided stock movement has already been linked to a transfer", nameof(transferIn));
- }
- transferOut.Type = StockMovementType.TransferOut;
- if (transaction.HasValue)
- {
- transferOut.Transaction = transaction.Value;
- }
- transferIn.LinkAsTransferIn(transferOut, transaction);
- }
- static StockMovement()
- {
- StockEntity.LinkStockDimensions<StockMovement>();
- LinkedProperties.Register<StockMovement, ProductStyleLink, Guid>(x=>x.Product.DefaultInstance.Style, x => x.ID, x => x.Style.ID);
- //LinkedProperties.Register<StockMovement, ProductLink, double>(x => x.Product, x => x.AverageCost, x => x.Cost);
- LinkedProperties.Register<StockMovement, JobScopeLink, Guid>(ass => ass.Job.DefaultScope, scope => scope.ID, ass => ass.JobScope.ID);
- LinkedProperties.Register<StockMovement, JobScopeLink, String>(ass => ass.Job.DefaultScope, scope => scope.Number, ass => ass.JobScope.Number);
- LinkedProperties.Register<StockMovement, JobScopeLink, String>(ass => ass.Job.DefaultScope, scope => scope.Description, ass => ass.JobScope.Description);
- }
- private static Column<StockMovement> unitsize = new Column<StockMovement>(x => x.Dimensions.Value);
- private static Column<StockMovement> issued = new Column<StockMovement>(x => x.Issued);
- private static Column<StockMovement> received = new Column<StockMovement>(x => x.Received);
- private bool bChanging;
- protected override void DoPropertyChanged(string name, object? before, object? after)
- {
- if (bChanging)
- return;
- if (unitsize.IsEqualTo(name))
- {
- bChanging = true;
- Qty = (Received - Issued) * (double)after;
- bChanging = false;
- }
- else if (issued.IsEqualTo(name))
- {
- bChanging = true;
- Units = Received - (double)after;
- Qty = Units * Dimensions.Value;
- bChanging = false;
- }
- else if (received.IsEqualTo(name))
- {
- bChanging = true;
- Units = ((double)after - Issued);
- Qty = Units * Dimensions.Value;
- bChanging = false;
- }
- base.DoPropertyChanged(name, before, after);
- }
- }
- }
|