|
|
@@ -7,46 +7,11 @@ using PRSClasses;
|
|
|
|
|
|
namespace Comal.Classes
|
|
|
{
|
|
|
- public class StockMovementDocumentCount : CoreAggregate<StockMovement, StockMovementBatchDocument, Guid>
|
|
|
- {
|
|
|
- public override Expression<Func<StockMovementBatchDocument, Guid>> Aggregate => x => x.ID;
|
|
|
-
|
|
|
- public override AggregateCalculation Calculation => AggregateCalculation.Count;
|
|
|
-
|
|
|
- public override Dictionary<Expression<Func<StockMovementBatchDocument, object>>, Expression<Func<StockMovement, object>>> Links =>
|
|
|
- new Dictionary<Expression<Func<StockMovementBatchDocument, object>>, Expression<Func<StockMovement, object>>>()
|
|
|
- {
|
|
|
- { StockMovementBatchDocument => StockMovementBatchDocument.EntityLink.ID, StockMovement => StockMovement.Batch.ID }
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
public class StockMovementLink : EntityLink<StockMovement>
|
|
|
{
|
|
|
[NullEditor]
|
|
|
public override Guid ID { get; set; }
|
|
|
}
|
|
|
-
|
|
|
- public class StockMovementValueFormula : IFormula<StockMovement, double>
|
|
|
- {
|
|
|
- public Expression<Func<StockMovement, double>> Value => x => x.Units;
|
|
|
-
|
|
|
- public Expression<Func<StockMovement, double>>[] Modifiers => new Expression<Func<StockMovement, double>>[] { x => x.Cost };
|
|
|
-
|
|
|
- public FormulaOperator Operator => FormulaOperator.Multiply;
|
|
|
-
|
|
|
- public FormulaType Type => FormulaType.Virtual;
|
|
|
- }
|
|
|
-
|
|
|
- public class StockMovementQuantityFormula : IFormula<StockMovement, double>
|
|
|
- {
|
|
|
- public Expression<Func<StockMovement, double>> Value => x => x.Units;
|
|
|
-
|
|
|
- public Expression<Func<StockMovement, double>>[] Modifiers => new Expression<Func<StockMovement, double>>[] { x => x.Dimensions.Value };
|
|
|
-
|
|
|
- public FormulaOperator Operator => FormulaOperator.Multiply;
|
|
|
-
|
|
|
- public FormulaType Type => FormulaType.Virtual;
|
|
|
- }
|
|
|
|
|
|
[UserTracking("Warehousing")]
|
|
|
public class StockMovement : StockEntity, IRemotable, IPersistent, IOneToMany<StockLocation>, IOneToMany<Product>,
|
|
|
@@ -118,16 +83,26 @@ namespace Comal.Classes
|
|
|
.Then(Constant(true))
|
|
|
.Else(Constant(false));
|
|
|
}
|
|
|
-
|
|
|
- // IsRemnant = Dimensions.Value < Product.Dimensions.Value
|
|
|
+ /// <summary>
|
|
|
+ /// IsRemnant = Dimensions.Value < Product.Dimensions.Value
|
|
|
+ /// </summary>
|
|
|
[CheckBoxEditor(Editable = Editable.Hidden)]
|
|
|
[ComplexFormula(typeof(IsRemnantCondition))]
|
|
|
[EditorSequence(7)]
|
|
|
public bool IsRemnant { get; set; }
|
|
|
|
|
|
- // Qty = Units * Dimensions.Value
|
|
|
+ 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)]
|
|
|
- [Formula(typeof(StockMovementQuantityFormula))]
|
|
|
+ [ComplexFormula(typeof(QuantityFormula))]
|
|
|
[DoubleEditor(Editable = Editable.Hidden, Summary = Summary.Sum)]
|
|
|
public double Qty { get; set; }
|
|
|
|
|
|
@@ -156,9 +131,23 @@ namespace Comal.Classes
|
|
|
[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; }
|
|
|
|
|
|
@@ -211,7 +200,15 @@ namespace Comal.Classes
|
|
|
|
|
|
public ActualCharge Charge { get; set; }
|
|
|
|
|
|
- [Aggregate(typeof(StockMovementDocumentCount))]
|
|
|
+ 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; }
|
|
|
|
|
|
@@ -226,11 +223,18 @@ namespace Comal.Classes
|
|
|
[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)]
|
|
|
- [Formula(typeof(StockMovementValueFormula))]
|
|
|
+ [ComplexFormula(typeof(ValueFormula))]
|
|
|
public double Value { get; set; } = 0.0;
|
|
|
|
|
|
[NullEditor]
|
|
|
@@ -248,6 +252,83 @@ namespace Comal.Classes
|
|
|
[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>();
|