Acumatica 您如何防止在分配上舍入>;请购单>;请求>;生产线>;订单数量?

Acumatica 您如何防止在分配上舍入>;请购单>;请求>;生产线>;订单数量?,acumatica,Acumatica,我已经尝试了所有的项目,但还是做不到。 有什么想法吗?点击定制->检查元素: 然后单击OrderQty网格列标题以标识DAC字段: 查找该字段的代码时,我们看到它用PXDBQuantity属性修饰: #region OrderQty public abstract class orderQty : PX.Data.IBqlField { } protected Decimal? _OrderQty; [PXDBQuantity(typeof(RQRequestLine.uOM), typeo

我已经尝试了所有的项目,但还是做不到。
有什么想法吗?

点击定制->检查元素:

然后单击OrderQty网格列标题以标识DAC字段:

查找该字段的代码时,我们看到它用PXDBQuantity属性修饰:

#region OrderQty
public abstract class orderQty : PX.Data.IBqlField
{
}
protected Decimal? _OrderQty;
[PXDBQuantity(typeof(RQRequestLine.uOM), typeof(RQRequestLine.baseOrderQty), HandleEmptyKey = true)]
[PXDefault(TypeCode.Decimal, "0.0")]
[PXUIField(DisplayName = "Order Qty.", Visibility = PXUIVisibility.Visible)]
[PXFormula(null, typeof(AddCalc<RQRequestLine.openQty>))]
public virtual Decimal? OrderQty
{
    get
    {
        return this._OrderQty;
    }
    set
    {
        this._OrderQty = value;
    }
}
#endregion
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PX.Data;
using PX.Objects.PO;
using PX.Objects.CS;
using PX.Objects.AP;
using PX.Objects.EP;
using PX.Objects.CR;
using System.Collections;
using PX.Objects.IN;
using PX.Objects.AR;
using PX.Objects.GL;
using PX.Objects.CM;
using PX.TM;
using PX.Objects;
using PX.Objects.RQ;
using System.Threading;

namespace PX.Objects.RQ
{
  
  public class RQRequestEntry_Extension:PXGraphExtension<RQRequestEntry>
  {
    // Redefine OrderQty attributes, replace PXDBQuantity attribute by 
    // your own PXDBQuantityNoRounding attribute which herits from
    // PXDBDecimalNoRounding instead PXDBDecimal.
    // Replace Math.Round calls in PXDBDecimalNoRounding by your own truncating logic
    [PXMergeAttributes(Method = MergeMethod.Replace)]
    [PXDBQuantityNoRounding(typeof(RQRequestLine.uOM), typeof(RQRequestLine.baseOrderQty), HandleEmptyKey = true)]
    [PXDefault(TypeCode.Decimal, "0.0")]
    [PXUIField(DisplayName = "Order Qty.", Visibility = PXUIVisibility.Visible)]
    [PXFormula(null, typeof(AddCalc<RQRequestLine.openQty>))]
    public virtual void RQRequestLine_OrderQty_CacheAttached(PXCache sender)
    {
    }
  }

  #region PXDBQuantityAttribute

  [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Class)]
  public class PXDBQuantityNoRoundingAttribute : PXDBDecimalNoRoundingAttribute, IPXFieldVerifyingSubscriber, IPXRowInsertingSubscriber
  {
    #region State
    protected int _ResultOrdinal;
    protected int _KeyOrdinal;
    protected Type _KeyField = null;
    protected Type _ResultField = null;
    protected bool _HandleEmptyKey = false;
    protected int? _OverridePrecision = null;

    public Type KeyField
    {
      get
      {
        return _KeyField;
      }
    }

    #endregion

    #region Ctor
    public PXDBQuantityNoRoundingAttribute()
    {
    }

    public PXDBQuantityNoRoundingAttribute(Type keyField, Type resultField)
    {
      _KeyField = keyField;
      _ResultField = resultField;
    }

    public PXDBQuantityNoRoundingAttribute(int precision, Type keyField, Type resultField)
    {
      _OverridePrecision = precision;
      _KeyField = keyField;
      _ResultField = resultField;
    }

    public bool HandleEmptyKey
    {
      set { this._HandleEmptyKey = value; }
      get { return this._HandleEmptyKey; }
    }

    #endregion

    #region Runtime
    public override void CacheAttached(PXCache sender)
    {
      base.CacheAttached(sender);

      _Precision = CommonSetupDecPl.Qty;

      if (_OverridePrecision != null)
        _Precision = _OverridePrecision.Value;

      if (_ResultField != null)
      {
        _ResultOrdinal = sender.GetFieldOrdinal(_ResultField.Name);
      }

      if (_KeyField != null)
      {
        _KeyOrdinal = sender.GetFieldOrdinal(_KeyField.Name);
        sender.Graph.FieldUpdated.AddHandler(BqlCommand.GetItemType(_KeyField), _KeyField.Name, KeyFieldUpdated);
      }
    }
    #endregion

    #region Implementation
    internal virtual object Select(PXCache cache, object data)
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes(data, _KeyField.Name))
      {
        if (attr is INUnitAttribute)
        {
          object value = cache.GetValue(data, _KeyField.Name);
          return PXSelectorAttribute.GetItem(cache, (PXSelectorAttribute)((INUnitAttribute)attr).SelectorAttr, data, value);
        }
      }
      return null;
    }

    protected virtual void CalcBaseQty(PXCache sender, PXFieldVerifyingEventArgs e)
    {
      decimal? resultval = null;

      if (_ResultField != null)
      {
        if (e.NewValue != null)
        {
          bool handled = false;
          if (this._HandleEmptyKey)
          {
            object value = sender.GetValue(e.Row, _KeyField.Name);
            if (String.IsNullOrEmpty((String)value))
            {
              resultval = (decimal)e.NewValue;
              handled = true;
            }
          }
          if (!handled)
          {
            if ((decimal)e.NewValue == 0)
            {
              resultval = 0m;
            }
            else
            {
            INUnit conv = (INUnit)Select(sender, e.Row);

            if (conv != null && conv.UnitRate != 0m)
            {
              _ensurePrecision(sender, e.Row);

                decimal? resultFieldCurrentValue = (decimal?)sender.GetValue(e.Row, this._ResultField.Name);
                bool reverseConvEqual = false;
                if (resultFieldCurrentValue != null)
                {
                  decimal revValue = Math.Round((resultFieldCurrentValue ?? 0m) * (conv.UnitMultDiv == "M" ? 1 / (decimal)conv.UnitRate : (decimal)conv.UnitRate), (int)_Precision, MidpointRounding.AwayFromZero);
                  if (revValue == (decimal)e.NewValue)
                    reverseConvEqual = true;
                }

                if (reverseConvEqual)
                  resultval = resultFieldCurrentValue;
                else
              resultval = Math.Round((decimal)e.NewValue * (conv.UnitMultDiv == "M" ? (decimal)conv.UnitRate : 1 / (decimal)conv.UnitRate), (int)_Precision, MidpointRounding.AwayFromZero);
            }

            if (conv == null && !e.ExternalCall)
            {
              throw new PXUnitConversionException((string)sender.GetValue(e.Row, _KeyField.Name)); 
            }
          }
        }
        }
        if (e.ExternalCall)
        {
          sender.SetValueExt(e.Row, this._ResultField.Name, resultval);
        }
        else
        {
          sender.SetValue(e.Row, this._ResultField.Name, resultval);
        }
      }
    }

    protected virtual void CalcBaseQty(PXCache sender, object data)
    {
      object NewValue = sender.GetValue(data, _FieldOrdinal);
      try
      {
        CalcBaseQty(sender, new PXFieldVerifyingEventArgs(data, NewValue, false));
      }
      catch (PXUnitConversionException)
      {
        sender.SetValue(data, _ResultField.Name, null);
      }
    }

    protected virtual void CalcTranQty(PXCache sender, object data)
    {
      decimal? resultval = null;

      if (_ResultField != null)
      {
        object NewValue = sender.GetValue(data, _ResultOrdinal);

        if (NewValue != null)
        {
          INUnit conv = (INUnit)Select(sender, data);

          if (conv != null && conv.UnitRate != 0m)
          {
            _ensurePrecision(sender, data);
            resultval = Math.Round((decimal)NewValue * (conv.UnitMultDiv == "M" ? 1 / (decimal)conv.UnitRate : (decimal)conv.UnitRate), (int)_Precision, MidpointRounding.AwayFromZero);
          }
        }
        sender.SetValue(data, _FieldOrdinal, resultval);
      }
    }

    public static void CalcBaseQty<TField>(PXCache cache, object data)
      where TField : class, IBqlField
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes<TField>(data))
      {
        if (attr is PXDBQuantityNoRoundingAttribute)
        {
          ((PXDBQuantityNoRoundingAttribute)attr).CalcBaseQty(cache, data);
          break;
        }
      }
    }

    public static void CalcTranQty<TField>(PXCache cache, object data)
      where TField : class, IBqlField
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes<TField>(data))
      {
        if (attr is PXDBQuantityNoRoundingAttribute)
        {
          ((PXDBQuantityNoRoundingAttribute)attr).CalcTranQty(cache, data);
          break;
        }
      }
    }

    public static decimal Round(decimal? value)
    {
      decimal value0 = value ?? 0m;
      return Math.Round(value0, CommonSetupDecPl.Qty, MidpointRounding.AwayFromZero);
    }

    public virtual void KeyFieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
    {
      CalcBaseQty(sender, e.Row);
    }

    public override void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
    {
            if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Delete) return;
      object NewValue = sender.GetValue(e.Row, _FieldOrdinal);
      CalcBaseQty(sender, new PXFieldVerifyingEventArgs(e.Row, NewValue, false));
    }

    public virtual void RowInserting(PXCache sender, PXRowInsertingEventArgs e)
    {
      CalcBaseQty(sender, e.Row);
    }

    public virtual void FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
    {
      PXFieldUpdatingEventArgs args = new PXFieldUpdatingEventArgs(e.Row, e.NewValue);
      if (!e.ExternalCall)
      {
        base.FieldUpdating(sender, args);
      }
      CalcBaseQty(sender, new PXFieldVerifyingEventArgs(args.Row, args.NewValue, true));
      e.NewValue = args.NewValue;
    }
    #endregion
  }

  #endregion

  #region PXDBDecimalAttribute
    /// <summary>Maps a DAC field of <tt>decimal?</tt> type to the database
    /// column of <tt>decimal</tt> type.</summary>
    /// <remarks>
    /// <para>The attribute is added to the value declaration of a DAC field.
    /// The field becomes bound to the database column with the same
    /// name.</para>
    /// <para>A minimum value, maximum value, and precision can be specified.
    /// The precision can be calculated at runtime using BQL. The default
    /// precision is 2.</para>
    /// </remarks>
    /// <example>
    /// Declaration of a DAC field with a specific precision is shown below.
    /// <code>
    /// [PXDBDecimal(6, MinValue = 0, MaxValue = 100)]
    /// public virtual decimal? Price { get; set; }</code>
    /// </example>
  [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Class | AttributeTargets.Method)]
  public class PXDBDecimalNoRoundingAttribute : PXDBFieldAttribute, IPXRowSelectingSubscriber, IPXCommandPreparingSubscriber, IPXFieldUpdatingSubscriber, IPXFieldSelectingSubscriber, IPXRowPersistingSubscriber
  {
    #region State

    public class DBDecimalProperties
    {
      public int? _scale;
      public int? _precision;
      public decimal? _maxValue;

      public int? Scale
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _scale;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }

      public int? Precision
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _precision;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }

      public decimal? MaxValue
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _maxValue;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }


      private ReaderWriterLock _sync = new ReaderWriterLock();

      public void Fill(Type table, string field)
      {
        _sync.AcquireReaderLock(-1);
        try {
          if (_scale != null && _precision != null && _maxValue != null)
            return;

          var lc = _sync.UpgradeToWriterLock(-1);
          try
          {
            if (_scale != null && _precision != null && _maxValue != null)
              return;

            var tableHeader = PXDatabase.Provider.GetTableStructure(table.Name);
            if (tableHeader != null)
            {
              var column = tableHeader
                .Columns.FirstOrDefault(c => string.Equals(c.Name, field, StringComparison.OrdinalIgnoreCase));
              if (column != null)
              {
                _scale = column.Scale;
                _precision = column.Precision;
                _maxValue = (decimal?)Math.Pow(10, (double)(column.Precision - column.Scale));
              }
              else
              {
                _scale = 29;
                _precision = 28;
                _maxValue = decimal.MaxValue;
              }
            }
          }
          catch
          {
          }
          finally
          {
            _sync.DowngradeFromWriterLock(ref lc);
          }
        }
        finally {
          _sync.ReleaseReaderLock();
        }
      }

      public bool IsSet
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _scale != null && _precision != null && _maxValue != null;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }
    }

    // because attributes instantiating by copying there will be only one instance for all instances of field.
        /// <exclude/>
    public DBDecimalProperties DBProperties { get; private set; }

    protected int? _Precision = 2;
    protected decimal _MinValue = decimal.MinValue;
    protected decimal _MaxValue = decimal.MaxValue;
    protected Type _Type;
    protected BqlCommand _Select;
        /// <summary>Gets or sets the minimum value for the field.</summary>
    public double MinValue
    {
      get
      {
        return (double)_MinValue;
      }
      set
      {
        _MinValue = (decimal)value;
      }
    }
        /// <summary>Gets or sets the minimum value for the field.</summary>
    public double MaxValue
    {
      get
      {
        return (double)_MaxValue;
      }
      set
      {
        _MaxValue = (decimal)value;
      }
    }
    #endregion

    #region Ctor
        /// <summary>Initializes a new instance with the default precision, which
        /// equals 2.</summary>
        /// <example>
        /// <code>
        /// [PXDBDecimal(MaxValue = 100, MinValue = 0)]
        /// [PXDefault(TypeCode.Decimal, "50.0")]
        /// [PXUIField(DisplayName = "Group/Document Discount Limit (%)")]
        /// public virtual Decimal? DiscountLimit { get; set; }
        /// </code>
        /// </example>
    public PXDBDecimalNoRoundingAttribute()
    {
      DBProperties = new DBDecimalProperties();
    }
        /// <summary>Initializes a new instance with the given
        /// precision.</summary>
        /// <example>
        /// <code>
        /// [PXDBDecimal(4)]
        /// [PXDefault(TypeCode.Decimal, "0.0")]
        /// public virtual Decimal? TaxTotal { get; set; }
        /// </code>
        /// </example>
    public PXDBDecimalNoRoundingAttribute(int precision)
      : this()
    {
      _Precision = precision;
    }

        /// <summary>Initializes a new instance with the precision calculated at
        /// runtime using a BQL query.</summary>
        /// <param name="type">A BQL query based on a class derived from
        /// <tt>IBqlSearch</tt> or <tt>IBqlField</tt>. For example, the parameter
        /// can be set to <tt>typeof(Search&lt;...&gt;)</tt>, or
        /// <tt>typeof(Table1.field)</tt>.</param>
        /// <example>
        /// The code below shows declaration of a DAC field with a precision calculated
        /// at runtime. The BQL query in this example will search for the <tt>Currency</tt> data record
        /// that satisfies the specified <tt>Where</tt> condition. The field precision will be
        /// set to the <tt>DecimalPlaces</tt> value from this data record.
        /// <code>
        /// [PXDBDecimal(typeof(
        ///     Search&lt;Currency.decimalPlaces,
        ///         Where&lt;Currency.curyID, Equal&lt;Current&lt;POCreateFilter.vendorID&gt;&gt;&gt;&gt;
        /// ))]
        /// public virtual decimal? OrderTotal { get; set; }
        /// </code>
        /// </example>
    public PXDBDecimalNoRoundingAttribute(Type type)
      : this()
    {
      if (type == null) {
        throw new PXArgumentException(type.Name, ErrorMessages.ArgumentNullException);
      }
      if (typeof(IBqlSearch).IsAssignableFrom(type)) {
        _Select = BqlCommand.CreateInstance(type);
        _Type = BqlCommand.GetItemType(((IBqlSearch)_Select).GetField());
      }
      else if (type.IsNested && typeof(IBqlField).IsAssignableFrom(type)) {
        _Type = BqlCommand.GetItemType(type);
        _Select = BqlCommand.CreateInstance(typeof(Search<>), type);
      }
      else {
        throw new PXArgumentException(type.Name, ErrorMessages.CantCreateForeignKeyReference, type);
      }
    }

    #endregion

    #region Runtime

        /// <summary>Sets the precision in the attribute instance that marks the
        /// field with the specified name in a particular data record.</summary>
        /// <param name="cache">The cache object to search for the attributes of
        /// <tt>PXDBDecimal</tt> type.</param>
        /// <param name="data">The data record the method is applied to.</param>
        /// <param name="name">The name of the field that is be marked with the
        /// attribute.</param>
        /// <param name="precision">The new precision value.</param>
        /// <example>
        /// The code below shows the <tt>RowSelected</tt> event handler (used to
        /// configure the UI at run time), in which you set the precision for the
        /// <tt>Qty</tt> field in the provided data record.
        /// <code>
        /// protected virtual void LotSerOptions_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
        /// {
        ///     LotSerOptions opt = (LotSerOptions)e.Row;
        ///     ...
        ///     PXDBDecimalAttribute.SetPrecision(sender, opt, "Qty", (opt.IsSerial == true ? 0 : INSetupDecPl.Qty));
        ///     ...
        /// }
        /// </code>
        /// </example>
    public static void SetPrecision(PXCache cache, object data, string name, int? precision)
    {
      if (data == null) {
        cache.SetAltered(name, true);
      }
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes(data, name)) {
        if (attr is PXDBDecimalNoRoundingAttribute) {
          ((PXDBDecimalNoRoundingAttribute)attr)._Precision = precision;
        }
      }
    }
        /// <summary>Sets the precision in the attribute instance that marks the
        /// field with the specified name in all data records in the cache
        /// object.</summary>
        /// <param name="cache">The cache object to search for the attributes of
        /// <tt>PXDBDecimal</tt> type.</param>
        /// <param name="name">The name of the field that is be marked with the
        /// attribute.</param>
        /// <param name="precision">The new precision value.</param>
    public static void SetPrecision(PXCache cache, string name, int? precision)
    {
      cache.SetAltered(name, true);
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes(name)) {
        if (attr is PXDBDecimalNoRoundingAttribute) {
          ((PXDBDecimalNoRoundingAttribute)attr)._Precision = precision;
        }
      }
    }
    #endregion

    #region Implementation

    protected string Check(object value)
    {
      if (value is decimal) {
        decimal val = Normalize((decimal)value);

        if (!DBProperties.IsSet) {
          DBProperties.Fill(_BqlTable, _DatabaseFieldName);
        }
        // if can`t read properties - ignoring check.
        if (DBProperties.IsSet) {
          if (Math.Abs(val) >= DBProperties.MaxValue) {
            return PXMessages.LocalizeFormat(ErrorMessages.InvalidDecimalValue, _FieldName);
          }
        }
      }

      return null;
    }

    protected decimal Normalize(decimal value)
    {
      return decimal.Round(value, 28);
    }

        /// <exclude/>
    public virtual void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
    {
      object val = sender.GetValue(e.Row, _FieldOrdinal);
      string error = Check(val);
      if (error != null) {
        if (sender.RaiseExceptionHandling(_FieldName, e.Row, null, new PXSetPropertyKeepPreviousException(error))) {
          throw new PXRowPersistingException(_FieldName, null, error);
        }
      }
    }

        /// <exclude/>
    protected override void PrepareCommandImpl(string dbFieldName, PXCommandPreparingEventArgs e)
    {
      base.PrepareCommandImpl(dbFieldName, e);
      e.DataType = PXDbType.Decimal;
      e.DataLength = 16;
    }

        /// <exclude/>
    public override void RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
    {
      if (e.Row != null) {
        sender.SetValue(e.Row, _FieldOrdinal, e.Record.GetDecimal(e.Position));
      }
      e.Position++;
    }
        /// <exclude/>
    public virtual void FieldUpdating(PXCache sender, PXFieldUpdatingEventArgs e)
    {
      if (e.NewValue is string) {
        decimal val;
        if (decimal.TryParse((string)e.NewValue, NumberStyles.Any, sender.Graph.Culture, out val)) {
          e.NewValue = val;
        }
        else {
          e.NewValue = null;
        }
      }
      if (e.NewValue != null) {
        _ensurePrecision(sender, e.Row);
        if (_Precision != null) {
          e.NewValue = Math.Round((decimal)e.NewValue, (int)_Precision, MidpointRounding.AwayFromZero);
        }
        string error = Check(e.NewValue);
        if (error != null) {
          throw new PXSetPropertyException(error);
        }
      }
    }
        /// <exclude/>
    public virtual void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
    {
      if (_AttributeLevel == PXAttributeLevel.Item || e.IsAltered) {
        _ensurePrecision(sender, e.Row);
        e.ReturnState = PXDecimalState.CreateInstance(e.ReturnState, _Precision, _FieldName, _IsKey, -1, _MinValue, _MaxValue);
      }
    }
    protected void _ensurePrecision(PXCache sender, object row)
    {
      if (_Type != null) {
        PXView view = sender.Graph.TypedViews.GetView(_Select, true);
        object item = null;
        try {
          List<object> list = view.SelectMultiBound(new object[] { row });
          if (list.Count > 0) item = list[0];
        }
        catch {
        }
        if (item != null) {
          int? prec = GetItemPrecision(view, item);
          if (prec != null)
            _Precision = prec;
        }
      }
    }

        /// <summary>Retrieves the precision value if it is set by a BQL query
        /// specified in the constructor, and sets its to all attribute instances
        /// in the cache object.</summary>
        /// <param name="cache">The cache object to search for the attributes of
        /// <tt>PXDBDecimal</tt> type.</param>
    public static void EnsurePrecision(PXCache cache)
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributesReadonly(null)) {
        PXDBDecimalNoRoundingAttribute decattr = attr as PXDBDecimalNoRoundingAttribute;
        if (decattr != null && decattr.AttributeLevel == PXAttributeLevel.Cache) {
          int? oldValue = decattr._Precision;
          try {
            decattr._Precision = null;
            decattr._ensurePrecision(cache, null);
            oldValue = (int)decattr._Precision;

            cache.SetAltered(decattr._FieldName, true);
            decattr._Type = null;
          }
          catch (InvalidOperationException) { }
          finally {
            decattr._Precision = oldValue;
          }
        }
      }
    }
    #endregion

    protected virtual int? GetItemPrecision(PXView view, object item)
    {
      if (item is PXResult) item = ((PXResult)item)[0];
      return item != null ? (short?)view.Cache.GetValue(item, ((IBqlSearch)_Select).GetField().Name) : null;
    }

  }

  #endregion
}
要修改舍入逻辑,可以创建自己的PXDBQuantity属性,并使用自定义截断逻辑而不是舍入从自己的PXDBDecimal属性继承它。然后,您可以使用自己的属性覆盖OrderQty PXDBQuantity属性:

#region OrderQty
public abstract class orderQty : PX.Data.IBqlField
{
}
protected Decimal? _OrderQty;
[PXDBQuantity(typeof(RQRequestLine.uOM), typeof(RQRequestLine.baseOrderQty), HandleEmptyKey = true)]
[PXDefault(TypeCode.Decimal, "0.0")]
[PXUIField(DisplayName = "Order Qty.", Visibility = PXUIVisibility.Visible)]
[PXFormula(null, typeof(AddCalc<RQRequestLine.openQty>))]
public virtual Decimal? OrderQty
{
    get
    {
        return this._OrderQty;
    }
    set
    {
        this._OrderQty = value;
    }
}
#endregion
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PX.Data;
using PX.Objects.PO;
using PX.Objects.CS;
using PX.Objects.AP;
using PX.Objects.EP;
using PX.Objects.CR;
using System.Collections;
using PX.Objects.IN;
using PX.Objects.AR;
using PX.Objects.GL;
using PX.Objects.CM;
using PX.TM;
using PX.Objects;
using PX.Objects.RQ;
using System.Threading;

namespace PX.Objects.RQ
{
  
  public class RQRequestEntry_Extension:PXGraphExtension<RQRequestEntry>
  {
    // Redefine OrderQty attributes, replace PXDBQuantity attribute by 
    // your own PXDBQuantityNoRounding attribute which herits from
    // PXDBDecimalNoRounding instead PXDBDecimal.
    // Replace Math.Round calls in PXDBDecimalNoRounding by your own truncating logic
    [PXMergeAttributes(Method = MergeMethod.Replace)]
    [PXDBQuantityNoRounding(typeof(RQRequestLine.uOM), typeof(RQRequestLine.baseOrderQty), HandleEmptyKey = true)]
    [PXDefault(TypeCode.Decimal, "0.0")]
    [PXUIField(DisplayName = "Order Qty.", Visibility = PXUIVisibility.Visible)]
    [PXFormula(null, typeof(AddCalc<RQRequestLine.openQty>))]
    public virtual void RQRequestLine_OrderQty_CacheAttached(PXCache sender)
    {
    }
  }

  #region PXDBQuantityAttribute

  [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Class)]
  public class PXDBQuantityNoRoundingAttribute : PXDBDecimalNoRoundingAttribute, IPXFieldVerifyingSubscriber, IPXRowInsertingSubscriber
  {
    #region State
    protected int _ResultOrdinal;
    protected int _KeyOrdinal;
    protected Type _KeyField = null;
    protected Type _ResultField = null;
    protected bool _HandleEmptyKey = false;
    protected int? _OverridePrecision = null;

    public Type KeyField
    {
      get
      {
        return _KeyField;
      }
    }

    #endregion

    #region Ctor
    public PXDBQuantityNoRoundingAttribute()
    {
    }

    public PXDBQuantityNoRoundingAttribute(Type keyField, Type resultField)
    {
      _KeyField = keyField;
      _ResultField = resultField;
    }

    public PXDBQuantityNoRoundingAttribute(int precision, Type keyField, Type resultField)
    {
      _OverridePrecision = precision;
      _KeyField = keyField;
      _ResultField = resultField;
    }

    public bool HandleEmptyKey
    {
      set { this._HandleEmptyKey = value; }
      get { return this._HandleEmptyKey; }
    }

    #endregion

    #region Runtime
    public override void CacheAttached(PXCache sender)
    {
      base.CacheAttached(sender);

      _Precision = CommonSetupDecPl.Qty;

      if (_OverridePrecision != null)
        _Precision = _OverridePrecision.Value;

      if (_ResultField != null)
      {
        _ResultOrdinal = sender.GetFieldOrdinal(_ResultField.Name);
      }

      if (_KeyField != null)
      {
        _KeyOrdinal = sender.GetFieldOrdinal(_KeyField.Name);
        sender.Graph.FieldUpdated.AddHandler(BqlCommand.GetItemType(_KeyField), _KeyField.Name, KeyFieldUpdated);
      }
    }
    #endregion

    #region Implementation
    internal virtual object Select(PXCache cache, object data)
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes(data, _KeyField.Name))
      {
        if (attr is INUnitAttribute)
        {
          object value = cache.GetValue(data, _KeyField.Name);
          return PXSelectorAttribute.GetItem(cache, (PXSelectorAttribute)((INUnitAttribute)attr).SelectorAttr, data, value);
        }
      }
      return null;
    }

    protected virtual void CalcBaseQty(PXCache sender, PXFieldVerifyingEventArgs e)
    {
      decimal? resultval = null;

      if (_ResultField != null)
      {
        if (e.NewValue != null)
        {
          bool handled = false;
          if (this._HandleEmptyKey)
          {
            object value = sender.GetValue(e.Row, _KeyField.Name);
            if (String.IsNullOrEmpty((String)value))
            {
              resultval = (decimal)e.NewValue;
              handled = true;
            }
          }
          if (!handled)
          {
            if ((decimal)e.NewValue == 0)
            {
              resultval = 0m;
            }
            else
            {
            INUnit conv = (INUnit)Select(sender, e.Row);

            if (conv != null && conv.UnitRate != 0m)
            {
              _ensurePrecision(sender, e.Row);

                decimal? resultFieldCurrentValue = (decimal?)sender.GetValue(e.Row, this._ResultField.Name);
                bool reverseConvEqual = false;
                if (resultFieldCurrentValue != null)
                {
                  decimal revValue = Math.Round((resultFieldCurrentValue ?? 0m) * (conv.UnitMultDiv == "M" ? 1 / (decimal)conv.UnitRate : (decimal)conv.UnitRate), (int)_Precision, MidpointRounding.AwayFromZero);
                  if (revValue == (decimal)e.NewValue)
                    reverseConvEqual = true;
                }

                if (reverseConvEqual)
                  resultval = resultFieldCurrentValue;
                else
              resultval = Math.Round((decimal)e.NewValue * (conv.UnitMultDiv == "M" ? (decimal)conv.UnitRate : 1 / (decimal)conv.UnitRate), (int)_Precision, MidpointRounding.AwayFromZero);
            }

            if (conv == null && !e.ExternalCall)
            {
              throw new PXUnitConversionException((string)sender.GetValue(e.Row, _KeyField.Name)); 
            }
          }
        }
        }
        if (e.ExternalCall)
        {
          sender.SetValueExt(e.Row, this._ResultField.Name, resultval);
        }
        else
        {
          sender.SetValue(e.Row, this._ResultField.Name, resultval);
        }
      }
    }

    protected virtual void CalcBaseQty(PXCache sender, object data)
    {
      object NewValue = sender.GetValue(data, _FieldOrdinal);
      try
      {
        CalcBaseQty(sender, new PXFieldVerifyingEventArgs(data, NewValue, false));
      }
      catch (PXUnitConversionException)
      {
        sender.SetValue(data, _ResultField.Name, null);
      }
    }

    protected virtual void CalcTranQty(PXCache sender, object data)
    {
      decimal? resultval = null;

      if (_ResultField != null)
      {
        object NewValue = sender.GetValue(data, _ResultOrdinal);

        if (NewValue != null)
        {
          INUnit conv = (INUnit)Select(sender, data);

          if (conv != null && conv.UnitRate != 0m)
          {
            _ensurePrecision(sender, data);
            resultval = Math.Round((decimal)NewValue * (conv.UnitMultDiv == "M" ? 1 / (decimal)conv.UnitRate : (decimal)conv.UnitRate), (int)_Precision, MidpointRounding.AwayFromZero);
          }
        }
        sender.SetValue(data, _FieldOrdinal, resultval);
      }
    }

    public static void CalcBaseQty<TField>(PXCache cache, object data)
      where TField : class, IBqlField
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes<TField>(data))
      {
        if (attr is PXDBQuantityNoRoundingAttribute)
        {
          ((PXDBQuantityNoRoundingAttribute)attr).CalcBaseQty(cache, data);
          break;
        }
      }
    }

    public static void CalcTranQty<TField>(PXCache cache, object data)
      where TField : class, IBqlField
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes<TField>(data))
      {
        if (attr is PXDBQuantityNoRoundingAttribute)
        {
          ((PXDBQuantityNoRoundingAttribute)attr).CalcTranQty(cache, data);
          break;
        }
      }
    }

    public static decimal Round(decimal? value)
    {
      decimal value0 = value ?? 0m;
      return Math.Round(value0, CommonSetupDecPl.Qty, MidpointRounding.AwayFromZero);
    }

    public virtual void KeyFieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
    {
      CalcBaseQty(sender, e.Row);
    }

    public override void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
    {
            if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Delete) return;
      object NewValue = sender.GetValue(e.Row, _FieldOrdinal);
      CalcBaseQty(sender, new PXFieldVerifyingEventArgs(e.Row, NewValue, false));
    }

    public virtual void RowInserting(PXCache sender, PXRowInsertingEventArgs e)
    {
      CalcBaseQty(sender, e.Row);
    }

    public virtual void FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
    {
      PXFieldUpdatingEventArgs args = new PXFieldUpdatingEventArgs(e.Row, e.NewValue);
      if (!e.ExternalCall)
      {
        base.FieldUpdating(sender, args);
      }
      CalcBaseQty(sender, new PXFieldVerifyingEventArgs(args.Row, args.NewValue, true));
      e.NewValue = args.NewValue;
    }
    #endregion
  }

  #endregion

  #region PXDBDecimalAttribute
    /// <summary>Maps a DAC field of <tt>decimal?</tt> type to the database
    /// column of <tt>decimal</tt> type.</summary>
    /// <remarks>
    /// <para>The attribute is added to the value declaration of a DAC field.
    /// The field becomes bound to the database column with the same
    /// name.</para>
    /// <para>A minimum value, maximum value, and precision can be specified.
    /// The precision can be calculated at runtime using BQL. The default
    /// precision is 2.</para>
    /// </remarks>
    /// <example>
    /// Declaration of a DAC field with a specific precision is shown below.
    /// <code>
    /// [PXDBDecimal(6, MinValue = 0, MaxValue = 100)]
    /// public virtual decimal? Price { get; set; }</code>
    /// </example>
  [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Class | AttributeTargets.Method)]
  public class PXDBDecimalNoRoundingAttribute : PXDBFieldAttribute, IPXRowSelectingSubscriber, IPXCommandPreparingSubscriber, IPXFieldUpdatingSubscriber, IPXFieldSelectingSubscriber, IPXRowPersistingSubscriber
  {
    #region State

    public class DBDecimalProperties
    {
      public int? _scale;
      public int? _precision;
      public decimal? _maxValue;

      public int? Scale
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _scale;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }

      public int? Precision
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _precision;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }

      public decimal? MaxValue
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _maxValue;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }


      private ReaderWriterLock _sync = new ReaderWriterLock();

      public void Fill(Type table, string field)
      {
        _sync.AcquireReaderLock(-1);
        try {
          if (_scale != null && _precision != null && _maxValue != null)
            return;

          var lc = _sync.UpgradeToWriterLock(-1);
          try
          {
            if (_scale != null && _precision != null && _maxValue != null)
              return;

            var tableHeader = PXDatabase.Provider.GetTableStructure(table.Name);
            if (tableHeader != null)
            {
              var column = tableHeader
                .Columns.FirstOrDefault(c => string.Equals(c.Name, field, StringComparison.OrdinalIgnoreCase));
              if (column != null)
              {
                _scale = column.Scale;
                _precision = column.Precision;
                _maxValue = (decimal?)Math.Pow(10, (double)(column.Precision - column.Scale));
              }
              else
              {
                _scale = 29;
                _precision = 28;
                _maxValue = decimal.MaxValue;
              }
            }
          }
          catch
          {
          }
          finally
          {
            _sync.DowngradeFromWriterLock(ref lc);
          }
        }
        finally {
          _sync.ReleaseReaderLock();
        }
      }

      public bool IsSet
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _scale != null && _precision != null && _maxValue != null;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }
    }

    // because attributes instantiating by copying there will be only one instance for all instances of field.
        /// <exclude/>
    public DBDecimalProperties DBProperties { get; private set; }

    protected int? _Precision = 2;
    protected decimal _MinValue = decimal.MinValue;
    protected decimal _MaxValue = decimal.MaxValue;
    protected Type _Type;
    protected BqlCommand _Select;
        /// <summary>Gets or sets the minimum value for the field.</summary>
    public double MinValue
    {
      get
      {
        return (double)_MinValue;
      }
      set
      {
        _MinValue = (decimal)value;
      }
    }
        /// <summary>Gets or sets the minimum value for the field.</summary>
    public double MaxValue
    {
      get
      {
        return (double)_MaxValue;
      }
      set
      {
        _MaxValue = (decimal)value;
      }
    }
    #endregion

    #region Ctor
        /// <summary>Initializes a new instance with the default precision, which
        /// equals 2.</summary>
        /// <example>
        /// <code>
        /// [PXDBDecimal(MaxValue = 100, MinValue = 0)]
        /// [PXDefault(TypeCode.Decimal, "50.0")]
        /// [PXUIField(DisplayName = "Group/Document Discount Limit (%)")]
        /// public virtual Decimal? DiscountLimit { get; set; }
        /// </code>
        /// </example>
    public PXDBDecimalNoRoundingAttribute()
    {
      DBProperties = new DBDecimalProperties();
    }
        /// <summary>Initializes a new instance with the given
        /// precision.</summary>
        /// <example>
        /// <code>
        /// [PXDBDecimal(4)]
        /// [PXDefault(TypeCode.Decimal, "0.0")]
        /// public virtual Decimal? TaxTotal { get; set; }
        /// </code>
        /// </example>
    public PXDBDecimalNoRoundingAttribute(int precision)
      : this()
    {
      _Precision = precision;
    }

        /// <summary>Initializes a new instance with the precision calculated at
        /// runtime using a BQL query.</summary>
        /// <param name="type">A BQL query based on a class derived from
        /// <tt>IBqlSearch</tt> or <tt>IBqlField</tt>. For example, the parameter
        /// can be set to <tt>typeof(Search&lt;...&gt;)</tt>, or
        /// <tt>typeof(Table1.field)</tt>.</param>
        /// <example>
        /// The code below shows declaration of a DAC field with a precision calculated
        /// at runtime. The BQL query in this example will search for the <tt>Currency</tt> data record
        /// that satisfies the specified <tt>Where</tt> condition. The field precision will be
        /// set to the <tt>DecimalPlaces</tt> value from this data record.
        /// <code>
        /// [PXDBDecimal(typeof(
        ///     Search&lt;Currency.decimalPlaces,
        ///         Where&lt;Currency.curyID, Equal&lt;Current&lt;POCreateFilter.vendorID&gt;&gt;&gt;&gt;
        /// ))]
        /// public virtual decimal? OrderTotal { get; set; }
        /// </code>
        /// </example>
    public PXDBDecimalNoRoundingAttribute(Type type)
      : this()
    {
      if (type == null) {
        throw new PXArgumentException(type.Name, ErrorMessages.ArgumentNullException);
      }
      if (typeof(IBqlSearch).IsAssignableFrom(type)) {
        _Select = BqlCommand.CreateInstance(type);
        _Type = BqlCommand.GetItemType(((IBqlSearch)_Select).GetField());
      }
      else if (type.IsNested && typeof(IBqlField).IsAssignableFrom(type)) {
        _Type = BqlCommand.GetItemType(type);
        _Select = BqlCommand.CreateInstance(typeof(Search<>), type);
      }
      else {
        throw new PXArgumentException(type.Name, ErrorMessages.CantCreateForeignKeyReference, type);
      }
    }

    #endregion

    #region Runtime

        /// <summary>Sets the precision in the attribute instance that marks the
        /// field with the specified name in a particular data record.</summary>
        /// <param name="cache">The cache object to search for the attributes of
        /// <tt>PXDBDecimal</tt> type.</param>
        /// <param name="data">The data record the method is applied to.</param>
        /// <param name="name">The name of the field that is be marked with the
        /// attribute.</param>
        /// <param name="precision">The new precision value.</param>
        /// <example>
        /// The code below shows the <tt>RowSelected</tt> event handler (used to
        /// configure the UI at run time), in which you set the precision for the
        /// <tt>Qty</tt> field in the provided data record.
        /// <code>
        /// protected virtual void LotSerOptions_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
        /// {
        ///     LotSerOptions opt = (LotSerOptions)e.Row;
        ///     ...
        ///     PXDBDecimalAttribute.SetPrecision(sender, opt, "Qty", (opt.IsSerial == true ? 0 : INSetupDecPl.Qty));
        ///     ...
        /// }
        /// </code>
        /// </example>
    public static void SetPrecision(PXCache cache, object data, string name, int? precision)
    {
      if (data == null) {
        cache.SetAltered(name, true);
      }
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes(data, name)) {
        if (attr is PXDBDecimalNoRoundingAttribute) {
          ((PXDBDecimalNoRoundingAttribute)attr)._Precision = precision;
        }
      }
    }
        /// <summary>Sets the precision in the attribute instance that marks the
        /// field with the specified name in all data records in the cache
        /// object.</summary>
        /// <param name="cache">The cache object to search for the attributes of
        /// <tt>PXDBDecimal</tt> type.</param>
        /// <param name="name">The name of the field that is be marked with the
        /// attribute.</param>
        /// <param name="precision">The new precision value.</param>
    public static void SetPrecision(PXCache cache, string name, int? precision)
    {
      cache.SetAltered(name, true);
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes(name)) {
        if (attr is PXDBDecimalNoRoundingAttribute) {
          ((PXDBDecimalNoRoundingAttribute)attr)._Precision = precision;
        }
      }
    }
    #endregion

    #region Implementation

    protected string Check(object value)
    {
      if (value is decimal) {
        decimal val = Normalize((decimal)value);

        if (!DBProperties.IsSet) {
          DBProperties.Fill(_BqlTable, _DatabaseFieldName);
        }
        // if can`t read properties - ignoring check.
        if (DBProperties.IsSet) {
          if (Math.Abs(val) >= DBProperties.MaxValue) {
            return PXMessages.LocalizeFormat(ErrorMessages.InvalidDecimalValue, _FieldName);
          }
        }
      }

      return null;
    }

    protected decimal Normalize(decimal value)
    {
      return decimal.Round(value, 28);
    }

        /// <exclude/>
    public virtual void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
    {
      object val = sender.GetValue(e.Row, _FieldOrdinal);
      string error = Check(val);
      if (error != null) {
        if (sender.RaiseExceptionHandling(_FieldName, e.Row, null, new PXSetPropertyKeepPreviousException(error))) {
          throw new PXRowPersistingException(_FieldName, null, error);
        }
      }
    }

        /// <exclude/>
    protected override void PrepareCommandImpl(string dbFieldName, PXCommandPreparingEventArgs e)
    {
      base.PrepareCommandImpl(dbFieldName, e);
      e.DataType = PXDbType.Decimal;
      e.DataLength = 16;
    }

        /// <exclude/>
    public override void RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
    {
      if (e.Row != null) {
        sender.SetValue(e.Row, _FieldOrdinal, e.Record.GetDecimal(e.Position));
      }
      e.Position++;
    }
        /// <exclude/>
    public virtual void FieldUpdating(PXCache sender, PXFieldUpdatingEventArgs e)
    {
      if (e.NewValue is string) {
        decimal val;
        if (decimal.TryParse((string)e.NewValue, NumberStyles.Any, sender.Graph.Culture, out val)) {
          e.NewValue = val;
        }
        else {
          e.NewValue = null;
        }
      }
      if (e.NewValue != null) {
        _ensurePrecision(sender, e.Row);
        if (_Precision != null) {
          e.NewValue = Math.Round((decimal)e.NewValue, (int)_Precision, MidpointRounding.AwayFromZero);
        }
        string error = Check(e.NewValue);
        if (error != null) {
          throw new PXSetPropertyException(error);
        }
      }
    }
        /// <exclude/>
    public virtual void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
    {
      if (_AttributeLevel == PXAttributeLevel.Item || e.IsAltered) {
        _ensurePrecision(sender, e.Row);
        e.ReturnState = PXDecimalState.CreateInstance(e.ReturnState, _Precision, _FieldName, _IsKey, -1, _MinValue, _MaxValue);
      }
    }
    protected void _ensurePrecision(PXCache sender, object row)
    {
      if (_Type != null) {
        PXView view = sender.Graph.TypedViews.GetView(_Select, true);
        object item = null;
        try {
          List<object> list = view.SelectMultiBound(new object[] { row });
          if (list.Count > 0) item = list[0];
        }
        catch {
        }
        if (item != null) {
          int? prec = GetItemPrecision(view, item);
          if (prec != null)
            _Precision = prec;
        }
      }
    }

        /// <summary>Retrieves the precision value if it is set by a BQL query
        /// specified in the constructor, and sets its to all attribute instances
        /// in the cache object.</summary>
        /// <param name="cache">The cache object to search for the attributes of
        /// <tt>PXDBDecimal</tt> type.</param>
    public static void EnsurePrecision(PXCache cache)
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributesReadonly(null)) {
        PXDBDecimalNoRoundingAttribute decattr = attr as PXDBDecimalNoRoundingAttribute;
        if (decattr != null && decattr.AttributeLevel == PXAttributeLevel.Cache) {
          int? oldValue = decattr._Precision;
          try {
            decattr._Precision = null;
            decattr._ensurePrecision(cache, null);
            oldValue = (int)decattr._Precision;

            cache.SetAltered(decattr._FieldName, true);
            decattr._Type = null;
          }
          catch (InvalidOperationException) { }
          finally {
            decattr._Precision = oldValue;
          }
        }
      }
    }
    #endregion

    protected virtual int? GetItemPrecision(PXView view, object item)
    {
      if (item is PXResult) item = ((PXResult)item)[0];
      return item != null ? (short?)view.Cache.GetValue(item, ((IBqlSearch)_Select).GetField().Name) : null;
    }

  }

  #endregion
}

单击自定义->检查元素:

然后单击OrderQty网格列标题以标识DAC字段:

查找该字段的代码时,我们看到它用PXDBQuantity属性修饰:

#region OrderQty
public abstract class orderQty : PX.Data.IBqlField
{
}
protected Decimal? _OrderQty;
[PXDBQuantity(typeof(RQRequestLine.uOM), typeof(RQRequestLine.baseOrderQty), HandleEmptyKey = true)]
[PXDefault(TypeCode.Decimal, "0.0")]
[PXUIField(DisplayName = "Order Qty.", Visibility = PXUIVisibility.Visible)]
[PXFormula(null, typeof(AddCalc<RQRequestLine.openQty>))]
public virtual Decimal? OrderQty
{
    get
    {
        return this._OrderQty;
    }
    set
    {
        this._OrderQty = value;
    }
}
#endregion
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PX.Data;
using PX.Objects.PO;
using PX.Objects.CS;
using PX.Objects.AP;
using PX.Objects.EP;
using PX.Objects.CR;
using System.Collections;
using PX.Objects.IN;
using PX.Objects.AR;
using PX.Objects.GL;
using PX.Objects.CM;
using PX.TM;
using PX.Objects;
using PX.Objects.RQ;
using System.Threading;

namespace PX.Objects.RQ
{
  
  public class RQRequestEntry_Extension:PXGraphExtension<RQRequestEntry>
  {
    // Redefine OrderQty attributes, replace PXDBQuantity attribute by 
    // your own PXDBQuantityNoRounding attribute which herits from
    // PXDBDecimalNoRounding instead PXDBDecimal.
    // Replace Math.Round calls in PXDBDecimalNoRounding by your own truncating logic
    [PXMergeAttributes(Method = MergeMethod.Replace)]
    [PXDBQuantityNoRounding(typeof(RQRequestLine.uOM), typeof(RQRequestLine.baseOrderQty), HandleEmptyKey = true)]
    [PXDefault(TypeCode.Decimal, "0.0")]
    [PXUIField(DisplayName = "Order Qty.", Visibility = PXUIVisibility.Visible)]
    [PXFormula(null, typeof(AddCalc<RQRequestLine.openQty>))]
    public virtual void RQRequestLine_OrderQty_CacheAttached(PXCache sender)
    {
    }
  }

  #region PXDBQuantityAttribute

  [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Class)]
  public class PXDBQuantityNoRoundingAttribute : PXDBDecimalNoRoundingAttribute, IPXFieldVerifyingSubscriber, IPXRowInsertingSubscriber
  {
    #region State
    protected int _ResultOrdinal;
    protected int _KeyOrdinal;
    protected Type _KeyField = null;
    protected Type _ResultField = null;
    protected bool _HandleEmptyKey = false;
    protected int? _OverridePrecision = null;

    public Type KeyField
    {
      get
      {
        return _KeyField;
      }
    }

    #endregion

    #region Ctor
    public PXDBQuantityNoRoundingAttribute()
    {
    }

    public PXDBQuantityNoRoundingAttribute(Type keyField, Type resultField)
    {
      _KeyField = keyField;
      _ResultField = resultField;
    }

    public PXDBQuantityNoRoundingAttribute(int precision, Type keyField, Type resultField)
    {
      _OverridePrecision = precision;
      _KeyField = keyField;
      _ResultField = resultField;
    }

    public bool HandleEmptyKey
    {
      set { this._HandleEmptyKey = value; }
      get { return this._HandleEmptyKey; }
    }

    #endregion

    #region Runtime
    public override void CacheAttached(PXCache sender)
    {
      base.CacheAttached(sender);

      _Precision = CommonSetupDecPl.Qty;

      if (_OverridePrecision != null)
        _Precision = _OverridePrecision.Value;

      if (_ResultField != null)
      {
        _ResultOrdinal = sender.GetFieldOrdinal(_ResultField.Name);
      }

      if (_KeyField != null)
      {
        _KeyOrdinal = sender.GetFieldOrdinal(_KeyField.Name);
        sender.Graph.FieldUpdated.AddHandler(BqlCommand.GetItemType(_KeyField), _KeyField.Name, KeyFieldUpdated);
      }
    }
    #endregion

    #region Implementation
    internal virtual object Select(PXCache cache, object data)
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes(data, _KeyField.Name))
      {
        if (attr is INUnitAttribute)
        {
          object value = cache.GetValue(data, _KeyField.Name);
          return PXSelectorAttribute.GetItem(cache, (PXSelectorAttribute)((INUnitAttribute)attr).SelectorAttr, data, value);
        }
      }
      return null;
    }

    protected virtual void CalcBaseQty(PXCache sender, PXFieldVerifyingEventArgs e)
    {
      decimal? resultval = null;

      if (_ResultField != null)
      {
        if (e.NewValue != null)
        {
          bool handled = false;
          if (this._HandleEmptyKey)
          {
            object value = sender.GetValue(e.Row, _KeyField.Name);
            if (String.IsNullOrEmpty((String)value))
            {
              resultval = (decimal)e.NewValue;
              handled = true;
            }
          }
          if (!handled)
          {
            if ((decimal)e.NewValue == 0)
            {
              resultval = 0m;
            }
            else
            {
            INUnit conv = (INUnit)Select(sender, e.Row);

            if (conv != null && conv.UnitRate != 0m)
            {
              _ensurePrecision(sender, e.Row);

                decimal? resultFieldCurrentValue = (decimal?)sender.GetValue(e.Row, this._ResultField.Name);
                bool reverseConvEqual = false;
                if (resultFieldCurrentValue != null)
                {
                  decimal revValue = Math.Round((resultFieldCurrentValue ?? 0m) * (conv.UnitMultDiv == "M" ? 1 / (decimal)conv.UnitRate : (decimal)conv.UnitRate), (int)_Precision, MidpointRounding.AwayFromZero);
                  if (revValue == (decimal)e.NewValue)
                    reverseConvEqual = true;
                }

                if (reverseConvEqual)
                  resultval = resultFieldCurrentValue;
                else
              resultval = Math.Round((decimal)e.NewValue * (conv.UnitMultDiv == "M" ? (decimal)conv.UnitRate : 1 / (decimal)conv.UnitRate), (int)_Precision, MidpointRounding.AwayFromZero);
            }

            if (conv == null && !e.ExternalCall)
            {
              throw new PXUnitConversionException((string)sender.GetValue(e.Row, _KeyField.Name)); 
            }
          }
        }
        }
        if (e.ExternalCall)
        {
          sender.SetValueExt(e.Row, this._ResultField.Name, resultval);
        }
        else
        {
          sender.SetValue(e.Row, this._ResultField.Name, resultval);
        }
      }
    }

    protected virtual void CalcBaseQty(PXCache sender, object data)
    {
      object NewValue = sender.GetValue(data, _FieldOrdinal);
      try
      {
        CalcBaseQty(sender, new PXFieldVerifyingEventArgs(data, NewValue, false));
      }
      catch (PXUnitConversionException)
      {
        sender.SetValue(data, _ResultField.Name, null);
      }
    }

    protected virtual void CalcTranQty(PXCache sender, object data)
    {
      decimal? resultval = null;

      if (_ResultField != null)
      {
        object NewValue = sender.GetValue(data, _ResultOrdinal);

        if (NewValue != null)
        {
          INUnit conv = (INUnit)Select(sender, data);

          if (conv != null && conv.UnitRate != 0m)
          {
            _ensurePrecision(sender, data);
            resultval = Math.Round((decimal)NewValue * (conv.UnitMultDiv == "M" ? 1 / (decimal)conv.UnitRate : (decimal)conv.UnitRate), (int)_Precision, MidpointRounding.AwayFromZero);
          }
        }
        sender.SetValue(data, _FieldOrdinal, resultval);
      }
    }

    public static void CalcBaseQty<TField>(PXCache cache, object data)
      where TField : class, IBqlField
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes<TField>(data))
      {
        if (attr is PXDBQuantityNoRoundingAttribute)
        {
          ((PXDBQuantityNoRoundingAttribute)attr).CalcBaseQty(cache, data);
          break;
        }
      }
    }

    public static void CalcTranQty<TField>(PXCache cache, object data)
      where TField : class, IBqlField
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes<TField>(data))
      {
        if (attr is PXDBQuantityNoRoundingAttribute)
        {
          ((PXDBQuantityNoRoundingAttribute)attr).CalcTranQty(cache, data);
          break;
        }
      }
    }

    public static decimal Round(decimal? value)
    {
      decimal value0 = value ?? 0m;
      return Math.Round(value0, CommonSetupDecPl.Qty, MidpointRounding.AwayFromZero);
    }

    public virtual void KeyFieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
    {
      CalcBaseQty(sender, e.Row);
    }

    public override void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
    {
            if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Delete) return;
      object NewValue = sender.GetValue(e.Row, _FieldOrdinal);
      CalcBaseQty(sender, new PXFieldVerifyingEventArgs(e.Row, NewValue, false));
    }

    public virtual void RowInserting(PXCache sender, PXRowInsertingEventArgs e)
    {
      CalcBaseQty(sender, e.Row);
    }

    public virtual void FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
    {
      PXFieldUpdatingEventArgs args = new PXFieldUpdatingEventArgs(e.Row, e.NewValue);
      if (!e.ExternalCall)
      {
        base.FieldUpdating(sender, args);
      }
      CalcBaseQty(sender, new PXFieldVerifyingEventArgs(args.Row, args.NewValue, true));
      e.NewValue = args.NewValue;
    }
    #endregion
  }

  #endregion

  #region PXDBDecimalAttribute
    /// <summary>Maps a DAC field of <tt>decimal?</tt> type to the database
    /// column of <tt>decimal</tt> type.</summary>
    /// <remarks>
    /// <para>The attribute is added to the value declaration of a DAC field.
    /// The field becomes bound to the database column with the same
    /// name.</para>
    /// <para>A minimum value, maximum value, and precision can be specified.
    /// The precision can be calculated at runtime using BQL. The default
    /// precision is 2.</para>
    /// </remarks>
    /// <example>
    /// Declaration of a DAC field with a specific precision is shown below.
    /// <code>
    /// [PXDBDecimal(6, MinValue = 0, MaxValue = 100)]
    /// public virtual decimal? Price { get; set; }</code>
    /// </example>
  [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Class | AttributeTargets.Method)]
  public class PXDBDecimalNoRoundingAttribute : PXDBFieldAttribute, IPXRowSelectingSubscriber, IPXCommandPreparingSubscriber, IPXFieldUpdatingSubscriber, IPXFieldSelectingSubscriber, IPXRowPersistingSubscriber
  {
    #region State

    public class DBDecimalProperties
    {
      public int? _scale;
      public int? _precision;
      public decimal? _maxValue;

      public int? Scale
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _scale;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }

      public int? Precision
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _precision;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }

      public decimal? MaxValue
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _maxValue;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }


      private ReaderWriterLock _sync = new ReaderWriterLock();

      public void Fill(Type table, string field)
      {
        _sync.AcquireReaderLock(-1);
        try {
          if (_scale != null && _precision != null && _maxValue != null)
            return;

          var lc = _sync.UpgradeToWriterLock(-1);
          try
          {
            if (_scale != null && _precision != null && _maxValue != null)
              return;

            var tableHeader = PXDatabase.Provider.GetTableStructure(table.Name);
            if (tableHeader != null)
            {
              var column = tableHeader
                .Columns.FirstOrDefault(c => string.Equals(c.Name, field, StringComparison.OrdinalIgnoreCase));
              if (column != null)
              {
                _scale = column.Scale;
                _precision = column.Precision;
                _maxValue = (decimal?)Math.Pow(10, (double)(column.Precision - column.Scale));
              }
              else
              {
                _scale = 29;
                _precision = 28;
                _maxValue = decimal.MaxValue;
              }
            }
          }
          catch
          {
          }
          finally
          {
            _sync.DowngradeFromWriterLock(ref lc);
          }
        }
        finally {
          _sync.ReleaseReaderLock();
        }
      }

      public bool IsSet
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _scale != null && _precision != null && _maxValue != null;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }
    }

    // because attributes instantiating by copying there will be only one instance for all instances of field.
        /// <exclude/>
    public DBDecimalProperties DBProperties { get; private set; }

    protected int? _Precision = 2;
    protected decimal _MinValue = decimal.MinValue;
    protected decimal _MaxValue = decimal.MaxValue;
    protected Type _Type;
    protected BqlCommand _Select;
        /// <summary>Gets or sets the minimum value for the field.</summary>
    public double MinValue
    {
      get
      {
        return (double)_MinValue;
      }
      set
      {
        _MinValue = (decimal)value;
      }
    }
        /// <summary>Gets or sets the minimum value for the field.</summary>
    public double MaxValue
    {
      get
      {
        return (double)_MaxValue;
      }
      set
      {
        _MaxValue = (decimal)value;
      }
    }
    #endregion

    #region Ctor
        /// <summary>Initializes a new instance with the default precision, which
        /// equals 2.</summary>
        /// <example>
        /// <code>
        /// [PXDBDecimal(MaxValue = 100, MinValue = 0)]
        /// [PXDefault(TypeCode.Decimal, "50.0")]
        /// [PXUIField(DisplayName = "Group/Document Discount Limit (%)")]
        /// public virtual Decimal? DiscountLimit { get; set; }
        /// </code>
        /// </example>
    public PXDBDecimalNoRoundingAttribute()
    {
      DBProperties = new DBDecimalProperties();
    }
        /// <summary>Initializes a new instance with the given
        /// precision.</summary>
        /// <example>
        /// <code>
        /// [PXDBDecimal(4)]
        /// [PXDefault(TypeCode.Decimal, "0.0")]
        /// public virtual Decimal? TaxTotal { get; set; }
        /// </code>
        /// </example>
    public PXDBDecimalNoRoundingAttribute(int precision)
      : this()
    {
      _Precision = precision;
    }

        /// <summary>Initializes a new instance with the precision calculated at
        /// runtime using a BQL query.</summary>
        /// <param name="type">A BQL query based on a class derived from
        /// <tt>IBqlSearch</tt> or <tt>IBqlField</tt>. For example, the parameter
        /// can be set to <tt>typeof(Search&lt;...&gt;)</tt>, or
        /// <tt>typeof(Table1.field)</tt>.</param>
        /// <example>
        /// The code below shows declaration of a DAC field with a precision calculated
        /// at runtime. The BQL query in this example will search for the <tt>Currency</tt> data record
        /// that satisfies the specified <tt>Where</tt> condition. The field precision will be
        /// set to the <tt>DecimalPlaces</tt> value from this data record.
        /// <code>
        /// [PXDBDecimal(typeof(
        ///     Search&lt;Currency.decimalPlaces,
        ///         Where&lt;Currency.curyID, Equal&lt;Current&lt;POCreateFilter.vendorID&gt;&gt;&gt;&gt;
        /// ))]
        /// public virtual decimal? OrderTotal { get; set; }
        /// </code>
        /// </example>
    public PXDBDecimalNoRoundingAttribute(Type type)
      : this()
    {
      if (type == null) {
        throw new PXArgumentException(type.Name, ErrorMessages.ArgumentNullException);
      }
      if (typeof(IBqlSearch).IsAssignableFrom(type)) {
        _Select = BqlCommand.CreateInstance(type);
        _Type = BqlCommand.GetItemType(((IBqlSearch)_Select).GetField());
      }
      else if (type.IsNested && typeof(IBqlField).IsAssignableFrom(type)) {
        _Type = BqlCommand.GetItemType(type);
        _Select = BqlCommand.CreateInstance(typeof(Search<>), type);
      }
      else {
        throw new PXArgumentException(type.Name, ErrorMessages.CantCreateForeignKeyReference, type);
      }
    }

    #endregion

    #region Runtime

        /// <summary>Sets the precision in the attribute instance that marks the
        /// field with the specified name in a particular data record.</summary>
        /// <param name="cache">The cache object to search for the attributes of
        /// <tt>PXDBDecimal</tt> type.</param>
        /// <param name="data">The data record the method is applied to.</param>
        /// <param name="name">The name of the field that is be marked with the
        /// attribute.</param>
        /// <param name="precision">The new precision value.</param>
        /// <example>
        /// The code below shows the <tt>RowSelected</tt> event handler (used to
        /// configure the UI at run time), in which you set the precision for the
        /// <tt>Qty</tt> field in the provided data record.
        /// <code>
        /// protected virtual void LotSerOptions_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
        /// {
        ///     LotSerOptions opt = (LotSerOptions)e.Row;
        ///     ...
        ///     PXDBDecimalAttribute.SetPrecision(sender, opt, "Qty", (opt.IsSerial == true ? 0 : INSetupDecPl.Qty));
        ///     ...
        /// }
        /// </code>
        /// </example>
    public static void SetPrecision(PXCache cache, object data, string name, int? precision)
    {
      if (data == null) {
        cache.SetAltered(name, true);
      }
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes(data, name)) {
        if (attr is PXDBDecimalNoRoundingAttribute) {
          ((PXDBDecimalNoRoundingAttribute)attr)._Precision = precision;
        }
      }
    }
        /// <summary>Sets the precision in the attribute instance that marks the
        /// field with the specified name in all data records in the cache
        /// object.</summary>
        /// <param name="cache">The cache object to search for the attributes of
        /// <tt>PXDBDecimal</tt> type.</param>
        /// <param name="name">The name of the field that is be marked with the
        /// attribute.</param>
        /// <param name="precision">The new precision value.</param>
    public static void SetPrecision(PXCache cache, string name, int? precision)
    {
      cache.SetAltered(name, true);
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes(name)) {
        if (attr is PXDBDecimalNoRoundingAttribute) {
          ((PXDBDecimalNoRoundingAttribute)attr)._Precision = precision;
        }
      }
    }
    #endregion

    #region Implementation

    protected string Check(object value)
    {
      if (value is decimal) {
        decimal val = Normalize((decimal)value);

        if (!DBProperties.IsSet) {
          DBProperties.Fill(_BqlTable, _DatabaseFieldName);
        }
        // if can`t read properties - ignoring check.
        if (DBProperties.IsSet) {
          if (Math.Abs(val) >= DBProperties.MaxValue) {
            return PXMessages.LocalizeFormat(ErrorMessages.InvalidDecimalValue, _FieldName);
          }
        }
      }

      return null;
    }

    protected decimal Normalize(decimal value)
    {
      return decimal.Round(value, 28);
    }

        /// <exclude/>
    public virtual void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
    {
      object val = sender.GetValue(e.Row, _FieldOrdinal);
      string error = Check(val);
      if (error != null) {
        if (sender.RaiseExceptionHandling(_FieldName, e.Row, null, new PXSetPropertyKeepPreviousException(error))) {
          throw new PXRowPersistingException(_FieldName, null, error);
        }
      }
    }

        /// <exclude/>
    protected override void PrepareCommandImpl(string dbFieldName, PXCommandPreparingEventArgs e)
    {
      base.PrepareCommandImpl(dbFieldName, e);
      e.DataType = PXDbType.Decimal;
      e.DataLength = 16;
    }

        /// <exclude/>
    public override void RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
    {
      if (e.Row != null) {
        sender.SetValue(e.Row, _FieldOrdinal, e.Record.GetDecimal(e.Position));
      }
      e.Position++;
    }
        /// <exclude/>
    public virtual void FieldUpdating(PXCache sender, PXFieldUpdatingEventArgs e)
    {
      if (e.NewValue is string) {
        decimal val;
        if (decimal.TryParse((string)e.NewValue, NumberStyles.Any, sender.Graph.Culture, out val)) {
          e.NewValue = val;
        }
        else {
          e.NewValue = null;
        }
      }
      if (e.NewValue != null) {
        _ensurePrecision(sender, e.Row);
        if (_Precision != null) {
          e.NewValue = Math.Round((decimal)e.NewValue, (int)_Precision, MidpointRounding.AwayFromZero);
        }
        string error = Check(e.NewValue);
        if (error != null) {
          throw new PXSetPropertyException(error);
        }
      }
    }
        /// <exclude/>
    public virtual void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
    {
      if (_AttributeLevel == PXAttributeLevel.Item || e.IsAltered) {
        _ensurePrecision(sender, e.Row);
        e.ReturnState = PXDecimalState.CreateInstance(e.ReturnState, _Precision, _FieldName, _IsKey, -1, _MinValue, _MaxValue);
      }
    }
    protected void _ensurePrecision(PXCache sender, object row)
    {
      if (_Type != null) {
        PXView view = sender.Graph.TypedViews.GetView(_Select, true);
        object item = null;
        try {
          List<object> list = view.SelectMultiBound(new object[] { row });
          if (list.Count > 0) item = list[0];
        }
        catch {
        }
        if (item != null) {
          int? prec = GetItemPrecision(view, item);
          if (prec != null)
            _Precision = prec;
        }
      }
    }

        /// <summary>Retrieves the precision value if it is set by a BQL query
        /// specified in the constructor, and sets its to all attribute instances
        /// in the cache object.</summary>
        /// <param name="cache">The cache object to search for the attributes of
        /// <tt>PXDBDecimal</tt> type.</param>
    public static void EnsurePrecision(PXCache cache)
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributesReadonly(null)) {
        PXDBDecimalNoRoundingAttribute decattr = attr as PXDBDecimalNoRoundingAttribute;
        if (decattr != null && decattr.AttributeLevel == PXAttributeLevel.Cache) {
          int? oldValue = decattr._Precision;
          try {
            decattr._Precision = null;
            decattr._ensurePrecision(cache, null);
            oldValue = (int)decattr._Precision;

            cache.SetAltered(decattr._FieldName, true);
            decattr._Type = null;
          }
          catch (InvalidOperationException) { }
          finally {
            decattr._Precision = oldValue;
          }
        }
      }
    }
    #endregion

    protected virtual int? GetItemPrecision(PXView view, object item)
    {
      if (item is PXResult) item = ((PXResult)item)[0];
      return item != null ? (short?)view.Cache.GetValue(item, ((IBqlSearch)_Select).GetField().Name) : null;
    }

  }

  #endregion
}
要修改舍入逻辑,可以创建自己的PXDBQuantity属性,并使用自定义截断逻辑而不是舍入从自己的PXDBDecimal属性继承它。然后,您可以使用自己的属性覆盖OrderQty PXDBQuantity属性:

#region OrderQty
public abstract class orderQty : PX.Data.IBqlField
{
}
protected Decimal? _OrderQty;
[PXDBQuantity(typeof(RQRequestLine.uOM), typeof(RQRequestLine.baseOrderQty), HandleEmptyKey = true)]
[PXDefault(TypeCode.Decimal, "0.0")]
[PXUIField(DisplayName = "Order Qty.", Visibility = PXUIVisibility.Visible)]
[PXFormula(null, typeof(AddCalc<RQRequestLine.openQty>))]
public virtual Decimal? OrderQty
{
    get
    {
        return this._OrderQty;
    }
    set
    {
        this._OrderQty = value;
    }
}
#endregion
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PX.Data;
using PX.Objects.PO;
using PX.Objects.CS;
using PX.Objects.AP;
using PX.Objects.EP;
using PX.Objects.CR;
using System.Collections;
using PX.Objects.IN;
using PX.Objects.AR;
using PX.Objects.GL;
using PX.Objects.CM;
using PX.TM;
using PX.Objects;
using PX.Objects.RQ;
using System.Threading;

namespace PX.Objects.RQ
{
  
  public class RQRequestEntry_Extension:PXGraphExtension<RQRequestEntry>
  {
    // Redefine OrderQty attributes, replace PXDBQuantity attribute by 
    // your own PXDBQuantityNoRounding attribute which herits from
    // PXDBDecimalNoRounding instead PXDBDecimal.
    // Replace Math.Round calls in PXDBDecimalNoRounding by your own truncating logic
    [PXMergeAttributes(Method = MergeMethod.Replace)]
    [PXDBQuantityNoRounding(typeof(RQRequestLine.uOM), typeof(RQRequestLine.baseOrderQty), HandleEmptyKey = true)]
    [PXDefault(TypeCode.Decimal, "0.0")]
    [PXUIField(DisplayName = "Order Qty.", Visibility = PXUIVisibility.Visible)]
    [PXFormula(null, typeof(AddCalc<RQRequestLine.openQty>))]
    public virtual void RQRequestLine_OrderQty_CacheAttached(PXCache sender)
    {
    }
  }

  #region PXDBQuantityAttribute

  [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Class)]
  public class PXDBQuantityNoRoundingAttribute : PXDBDecimalNoRoundingAttribute, IPXFieldVerifyingSubscriber, IPXRowInsertingSubscriber
  {
    #region State
    protected int _ResultOrdinal;
    protected int _KeyOrdinal;
    protected Type _KeyField = null;
    protected Type _ResultField = null;
    protected bool _HandleEmptyKey = false;
    protected int? _OverridePrecision = null;

    public Type KeyField
    {
      get
      {
        return _KeyField;
      }
    }

    #endregion

    #region Ctor
    public PXDBQuantityNoRoundingAttribute()
    {
    }

    public PXDBQuantityNoRoundingAttribute(Type keyField, Type resultField)
    {
      _KeyField = keyField;
      _ResultField = resultField;
    }

    public PXDBQuantityNoRoundingAttribute(int precision, Type keyField, Type resultField)
    {
      _OverridePrecision = precision;
      _KeyField = keyField;
      _ResultField = resultField;
    }

    public bool HandleEmptyKey
    {
      set { this._HandleEmptyKey = value; }
      get { return this._HandleEmptyKey; }
    }

    #endregion

    #region Runtime
    public override void CacheAttached(PXCache sender)
    {
      base.CacheAttached(sender);

      _Precision = CommonSetupDecPl.Qty;

      if (_OverridePrecision != null)
        _Precision = _OverridePrecision.Value;

      if (_ResultField != null)
      {
        _ResultOrdinal = sender.GetFieldOrdinal(_ResultField.Name);
      }

      if (_KeyField != null)
      {
        _KeyOrdinal = sender.GetFieldOrdinal(_KeyField.Name);
        sender.Graph.FieldUpdated.AddHandler(BqlCommand.GetItemType(_KeyField), _KeyField.Name, KeyFieldUpdated);
      }
    }
    #endregion

    #region Implementation
    internal virtual object Select(PXCache cache, object data)
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes(data, _KeyField.Name))
      {
        if (attr is INUnitAttribute)
        {
          object value = cache.GetValue(data, _KeyField.Name);
          return PXSelectorAttribute.GetItem(cache, (PXSelectorAttribute)((INUnitAttribute)attr).SelectorAttr, data, value);
        }
      }
      return null;
    }

    protected virtual void CalcBaseQty(PXCache sender, PXFieldVerifyingEventArgs e)
    {
      decimal? resultval = null;

      if (_ResultField != null)
      {
        if (e.NewValue != null)
        {
          bool handled = false;
          if (this._HandleEmptyKey)
          {
            object value = sender.GetValue(e.Row, _KeyField.Name);
            if (String.IsNullOrEmpty((String)value))
            {
              resultval = (decimal)e.NewValue;
              handled = true;
            }
          }
          if (!handled)
          {
            if ((decimal)e.NewValue == 0)
            {
              resultval = 0m;
            }
            else
            {
            INUnit conv = (INUnit)Select(sender, e.Row);

            if (conv != null && conv.UnitRate != 0m)
            {
              _ensurePrecision(sender, e.Row);

                decimal? resultFieldCurrentValue = (decimal?)sender.GetValue(e.Row, this._ResultField.Name);
                bool reverseConvEqual = false;
                if (resultFieldCurrentValue != null)
                {
                  decimal revValue = Math.Round((resultFieldCurrentValue ?? 0m) * (conv.UnitMultDiv == "M" ? 1 / (decimal)conv.UnitRate : (decimal)conv.UnitRate), (int)_Precision, MidpointRounding.AwayFromZero);
                  if (revValue == (decimal)e.NewValue)
                    reverseConvEqual = true;
                }

                if (reverseConvEqual)
                  resultval = resultFieldCurrentValue;
                else
              resultval = Math.Round((decimal)e.NewValue * (conv.UnitMultDiv == "M" ? (decimal)conv.UnitRate : 1 / (decimal)conv.UnitRate), (int)_Precision, MidpointRounding.AwayFromZero);
            }

            if (conv == null && !e.ExternalCall)
            {
              throw new PXUnitConversionException((string)sender.GetValue(e.Row, _KeyField.Name)); 
            }
          }
        }
        }
        if (e.ExternalCall)
        {
          sender.SetValueExt(e.Row, this._ResultField.Name, resultval);
        }
        else
        {
          sender.SetValue(e.Row, this._ResultField.Name, resultval);
        }
      }
    }

    protected virtual void CalcBaseQty(PXCache sender, object data)
    {
      object NewValue = sender.GetValue(data, _FieldOrdinal);
      try
      {
        CalcBaseQty(sender, new PXFieldVerifyingEventArgs(data, NewValue, false));
      }
      catch (PXUnitConversionException)
      {
        sender.SetValue(data, _ResultField.Name, null);
      }
    }

    protected virtual void CalcTranQty(PXCache sender, object data)
    {
      decimal? resultval = null;

      if (_ResultField != null)
      {
        object NewValue = sender.GetValue(data, _ResultOrdinal);

        if (NewValue != null)
        {
          INUnit conv = (INUnit)Select(sender, data);

          if (conv != null && conv.UnitRate != 0m)
          {
            _ensurePrecision(sender, data);
            resultval = Math.Round((decimal)NewValue * (conv.UnitMultDiv == "M" ? 1 / (decimal)conv.UnitRate : (decimal)conv.UnitRate), (int)_Precision, MidpointRounding.AwayFromZero);
          }
        }
        sender.SetValue(data, _FieldOrdinal, resultval);
      }
    }

    public static void CalcBaseQty<TField>(PXCache cache, object data)
      where TField : class, IBqlField
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes<TField>(data))
      {
        if (attr is PXDBQuantityNoRoundingAttribute)
        {
          ((PXDBQuantityNoRoundingAttribute)attr).CalcBaseQty(cache, data);
          break;
        }
      }
    }

    public static void CalcTranQty<TField>(PXCache cache, object data)
      where TField : class, IBqlField
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes<TField>(data))
      {
        if (attr is PXDBQuantityNoRoundingAttribute)
        {
          ((PXDBQuantityNoRoundingAttribute)attr).CalcTranQty(cache, data);
          break;
        }
      }
    }

    public static decimal Round(decimal? value)
    {
      decimal value0 = value ?? 0m;
      return Math.Round(value0, CommonSetupDecPl.Qty, MidpointRounding.AwayFromZero);
    }

    public virtual void KeyFieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
    {
      CalcBaseQty(sender, e.Row);
    }

    public override void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
    {
            if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Delete) return;
      object NewValue = sender.GetValue(e.Row, _FieldOrdinal);
      CalcBaseQty(sender, new PXFieldVerifyingEventArgs(e.Row, NewValue, false));
    }

    public virtual void RowInserting(PXCache sender, PXRowInsertingEventArgs e)
    {
      CalcBaseQty(sender, e.Row);
    }

    public virtual void FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
    {
      PXFieldUpdatingEventArgs args = new PXFieldUpdatingEventArgs(e.Row, e.NewValue);
      if (!e.ExternalCall)
      {
        base.FieldUpdating(sender, args);
      }
      CalcBaseQty(sender, new PXFieldVerifyingEventArgs(args.Row, args.NewValue, true));
      e.NewValue = args.NewValue;
    }
    #endregion
  }

  #endregion

  #region PXDBDecimalAttribute
    /// <summary>Maps a DAC field of <tt>decimal?</tt> type to the database
    /// column of <tt>decimal</tt> type.</summary>
    /// <remarks>
    /// <para>The attribute is added to the value declaration of a DAC field.
    /// The field becomes bound to the database column with the same
    /// name.</para>
    /// <para>A minimum value, maximum value, and precision can be specified.
    /// The precision can be calculated at runtime using BQL. The default
    /// precision is 2.</para>
    /// </remarks>
    /// <example>
    /// Declaration of a DAC field with a specific precision is shown below.
    /// <code>
    /// [PXDBDecimal(6, MinValue = 0, MaxValue = 100)]
    /// public virtual decimal? Price { get; set; }</code>
    /// </example>
  [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Class | AttributeTargets.Method)]
  public class PXDBDecimalNoRoundingAttribute : PXDBFieldAttribute, IPXRowSelectingSubscriber, IPXCommandPreparingSubscriber, IPXFieldUpdatingSubscriber, IPXFieldSelectingSubscriber, IPXRowPersistingSubscriber
  {
    #region State

    public class DBDecimalProperties
    {
      public int? _scale;
      public int? _precision;
      public decimal? _maxValue;

      public int? Scale
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _scale;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }

      public int? Precision
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _precision;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }

      public decimal? MaxValue
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _maxValue;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }


      private ReaderWriterLock _sync = new ReaderWriterLock();

      public void Fill(Type table, string field)
      {
        _sync.AcquireReaderLock(-1);
        try {
          if (_scale != null && _precision != null && _maxValue != null)
            return;

          var lc = _sync.UpgradeToWriterLock(-1);
          try
          {
            if (_scale != null && _precision != null && _maxValue != null)
              return;

            var tableHeader = PXDatabase.Provider.GetTableStructure(table.Name);
            if (tableHeader != null)
            {
              var column = tableHeader
                .Columns.FirstOrDefault(c => string.Equals(c.Name, field, StringComparison.OrdinalIgnoreCase));
              if (column != null)
              {
                _scale = column.Scale;
                _precision = column.Precision;
                _maxValue = (decimal?)Math.Pow(10, (double)(column.Precision - column.Scale));
              }
              else
              {
                _scale = 29;
                _precision = 28;
                _maxValue = decimal.MaxValue;
              }
            }
          }
          catch
          {
          }
          finally
          {
            _sync.DowngradeFromWriterLock(ref lc);
          }
        }
        finally {
          _sync.ReleaseReaderLock();
        }
      }

      public bool IsSet
      {
        get
        {
          _sync.AcquireReaderLock(-1);
          try {
            return _scale != null && _precision != null && _maxValue != null;
          }
          finally {
            _sync.ReleaseReaderLock();
          }
        }
      }
    }

    // because attributes instantiating by copying there will be only one instance for all instances of field.
        /// <exclude/>
    public DBDecimalProperties DBProperties { get; private set; }

    protected int? _Precision = 2;
    protected decimal _MinValue = decimal.MinValue;
    protected decimal _MaxValue = decimal.MaxValue;
    protected Type _Type;
    protected BqlCommand _Select;
        /// <summary>Gets or sets the minimum value for the field.</summary>
    public double MinValue
    {
      get
      {
        return (double)_MinValue;
      }
      set
      {
        _MinValue = (decimal)value;
      }
    }
        /// <summary>Gets or sets the minimum value for the field.</summary>
    public double MaxValue
    {
      get
      {
        return (double)_MaxValue;
      }
      set
      {
        _MaxValue = (decimal)value;
      }
    }
    #endregion

    #region Ctor
        /// <summary>Initializes a new instance with the default precision, which
        /// equals 2.</summary>
        /// <example>
        /// <code>
        /// [PXDBDecimal(MaxValue = 100, MinValue = 0)]
        /// [PXDefault(TypeCode.Decimal, "50.0")]
        /// [PXUIField(DisplayName = "Group/Document Discount Limit (%)")]
        /// public virtual Decimal? DiscountLimit { get; set; }
        /// </code>
        /// </example>
    public PXDBDecimalNoRoundingAttribute()
    {
      DBProperties = new DBDecimalProperties();
    }
        /// <summary>Initializes a new instance with the given
        /// precision.</summary>
        /// <example>
        /// <code>
        /// [PXDBDecimal(4)]
        /// [PXDefault(TypeCode.Decimal, "0.0")]
        /// public virtual Decimal? TaxTotal { get; set; }
        /// </code>
        /// </example>
    public PXDBDecimalNoRoundingAttribute(int precision)
      : this()
    {
      _Precision = precision;
    }

        /// <summary>Initializes a new instance with the precision calculated at
        /// runtime using a BQL query.</summary>
        /// <param name="type">A BQL query based on a class derived from
        /// <tt>IBqlSearch</tt> or <tt>IBqlField</tt>. For example, the parameter
        /// can be set to <tt>typeof(Search&lt;...&gt;)</tt>, or
        /// <tt>typeof(Table1.field)</tt>.</param>
        /// <example>
        /// The code below shows declaration of a DAC field with a precision calculated
        /// at runtime. The BQL query in this example will search for the <tt>Currency</tt> data record
        /// that satisfies the specified <tt>Where</tt> condition. The field precision will be
        /// set to the <tt>DecimalPlaces</tt> value from this data record.
        /// <code>
        /// [PXDBDecimal(typeof(
        ///     Search&lt;Currency.decimalPlaces,
        ///         Where&lt;Currency.curyID, Equal&lt;Current&lt;POCreateFilter.vendorID&gt;&gt;&gt;&gt;
        /// ))]
        /// public virtual decimal? OrderTotal { get; set; }
        /// </code>
        /// </example>
    public PXDBDecimalNoRoundingAttribute(Type type)
      : this()
    {
      if (type == null) {
        throw new PXArgumentException(type.Name, ErrorMessages.ArgumentNullException);
      }
      if (typeof(IBqlSearch).IsAssignableFrom(type)) {
        _Select = BqlCommand.CreateInstance(type);
        _Type = BqlCommand.GetItemType(((IBqlSearch)_Select).GetField());
      }
      else if (type.IsNested && typeof(IBqlField).IsAssignableFrom(type)) {
        _Type = BqlCommand.GetItemType(type);
        _Select = BqlCommand.CreateInstance(typeof(Search<>), type);
      }
      else {
        throw new PXArgumentException(type.Name, ErrorMessages.CantCreateForeignKeyReference, type);
      }
    }

    #endregion

    #region Runtime

        /// <summary>Sets the precision in the attribute instance that marks the
        /// field with the specified name in a particular data record.</summary>
        /// <param name="cache">The cache object to search for the attributes of
        /// <tt>PXDBDecimal</tt> type.</param>
        /// <param name="data">The data record the method is applied to.</param>
        /// <param name="name">The name of the field that is be marked with the
        /// attribute.</param>
        /// <param name="precision">The new precision value.</param>
        /// <example>
        /// The code below shows the <tt>RowSelected</tt> event handler (used to
        /// configure the UI at run time), in which you set the precision for the
        /// <tt>Qty</tt> field in the provided data record.
        /// <code>
        /// protected virtual void LotSerOptions_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
        /// {
        ///     LotSerOptions opt = (LotSerOptions)e.Row;
        ///     ...
        ///     PXDBDecimalAttribute.SetPrecision(sender, opt, "Qty", (opt.IsSerial == true ? 0 : INSetupDecPl.Qty));
        ///     ...
        /// }
        /// </code>
        /// </example>
    public static void SetPrecision(PXCache cache, object data, string name, int? precision)
    {
      if (data == null) {
        cache.SetAltered(name, true);
      }
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes(data, name)) {
        if (attr is PXDBDecimalNoRoundingAttribute) {
          ((PXDBDecimalNoRoundingAttribute)attr)._Precision = precision;
        }
      }
    }
        /// <summary>Sets the precision in the attribute instance that marks the
        /// field with the specified name in all data records in the cache
        /// object.</summary>
        /// <param name="cache">The cache object to search for the attributes of
        /// <tt>PXDBDecimal</tt> type.</param>
        /// <param name="name">The name of the field that is be marked with the
        /// attribute.</param>
        /// <param name="precision">The new precision value.</param>
    public static void SetPrecision(PXCache cache, string name, int? precision)
    {
      cache.SetAltered(name, true);
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributes(name)) {
        if (attr is PXDBDecimalNoRoundingAttribute) {
          ((PXDBDecimalNoRoundingAttribute)attr)._Precision = precision;
        }
      }
    }
    #endregion

    #region Implementation

    protected string Check(object value)
    {
      if (value is decimal) {
        decimal val = Normalize((decimal)value);

        if (!DBProperties.IsSet) {
          DBProperties.Fill(_BqlTable, _DatabaseFieldName);
        }
        // if can`t read properties - ignoring check.
        if (DBProperties.IsSet) {
          if (Math.Abs(val) >= DBProperties.MaxValue) {
            return PXMessages.LocalizeFormat(ErrorMessages.InvalidDecimalValue, _FieldName);
          }
        }
      }

      return null;
    }

    protected decimal Normalize(decimal value)
    {
      return decimal.Round(value, 28);
    }

        /// <exclude/>
    public virtual void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
    {
      object val = sender.GetValue(e.Row, _FieldOrdinal);
      string error = Check(val);
      if (error != null) {
        if (sender.RaiseExceptionHandling(_FieldName, e.Row, null, new PXSetPropertyKeepPreviousException(error))) {
          throw new PXRowPersistingException(_FieldName, null, error);
        }
      }
    }

        /// <exclude/>
    protected override void PrepareCommandImpl(string dbFieldName, PXCommandPreparingEventArgs e)
    {
      base.PrepareCommandImpl(dbFieldName, e);
      e.DataType = PXDbType.Decimal;
      e.DataLength = 16;
    }

        /// <exclude/>
    public override void RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
    {
      if (e.Row != null) {
        sender.SetValue(e.Row, _FieldOrdinal, e.Record.GetDecimal(e.Position));
      }
      e.Position++;
    }
        /// <exclude/>
    public virtual void FieldUpdating(PXCache sender, PXFieldUpdatingEventArgs e)
    {
      if (e.NewValue is string) {
        decimal val;
        if (decimal.TryParse((string)e.NewValue, NumberStyles.Any, sender.Graph.Culture, out val)) {
          e.NewValue = val;
        }
        else {
          e.NewValue = null;
        }
      }
      if (e.NewValue != null) {
        _ensurePrecision(sender, e.Row);
        if (_Precision != null) {
          e.NewValue = Math.Round((decimal)e.NewValue, (int)_Precision, MidpointRounding.AwayFromZero);
        }
        string error = Check(e.NewValue);
        if (error != null) {
          throw new PXSetPropertyException(error);
        }
      }
    }
        /// <exclude/>
    public virtual void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
    {
      if (_AttributeLevel == PXAttributeLevel.Item || e.IsAltered) {
        _ensurePrecision(sender, e.Row);
        e.ReturnState = PXDecimalState.CreateInstance(e.ReturnState, _Precision, _FieldName, _IsKey, -1, _MinValue, _MaxValue);
      }
    }
    protected void _ensurePrecision(PXCache sender, object row)
    {
      if (_Type != null) {
        PXView view = sender.Graph.TypedViews.GetView(_Select, true);
        object item = null;
        try {
          List<object> list = view.SelectMultiBound(new object[] { row });
          if (list.Count > 0) item = list[0];
        }
        catch {
        }
        if (item != null) {
          int? prec = GetItemPrecision(view, item);
          if (prec != null)
            _Precision = prec;
        }
      }
    }

        /// <summary>Retrieves the precision value if it is set by a BQL query
        /// specified in the constructor, and sets its to all attribute instances
        /// in the cache object.</summary>
        /// <param name="cache">The cache object to search for the attributes of
        /// <tt>PXDBDecimal</tt> type.</param>
    public static void EnsurePrecision(PXCache cache)
    {
      foreach (PXEventSubscriberAttribute attr in cache.GetAttributesReadonly(null)) {
        PXDBDecimalNoRoundingAttribute decattr = attr as PXDBDecimalNoRoundingAttribute;
        if (decattr != null && decattr.AttributeLevel == PXAttributeLevel.Cache) {
          int? oldValue = decattr._Precision;
          try {
            decattr._Precision = null;
            decattr._ensurePrecision(cache, null);
            oldValue = (int)decattr._Precision;

            cache.SetAltered(decattr._FieldName, true);
            decattr._Type = null;
          }
          catch (InvalidOperationException) { }
          finally {
            decattr._Precision = oldValue;
          }
        }
      }
    }
    #endregion

    protected virtual int? GetItemPrecision(PXView view, object item)
    {
      if (item is PXResult) item = ((PXResult)item)[0];
      return item != null ? (short?)view.Cache.GetValue(item, ((IBqlSearch)_Select).GetField().Name) : null;
    }

  }

  #endregion
}

请共享您检查的事件我尝试过,行持久化,行持久化,订单数量\u字段验证,订单数量\u字段更新,计量单位字段更新,计量单位字段验证我提到计量单位,因为我在发生舍入的源代码中看到它。谢谢:)请共享您检查的事件我尝试过,行持久化,行持久化,订单数量字段验证,订单数量字段更新,计量单位字段更新,计量单位字段验证我提到计量单位,因为我在发生舍入的源代码中看到它。谢谢:)我认为“防止舍入”是指截断而不是舍入。如果您真正想要的是更改用于计算的十进制数,那么您需要研究修改精度而不是Math.Round。我假设“防止舍入”意味着截断而不是舍入。如果您真正想要的是更改用于计算的十进制数,那么您需要研究修改精度,而不是Math.Round。