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<...>)</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<Currency.decimalPlaces,
/// Where<Currency.curyID, Equal<Current<POCreateFilter.vendorID>>>>
/// ))]
/// 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<...>)</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<Currency.decimalPlaces,
/// Where<Currency.curyID, Equal<Current<POCreateFilter.vendorID>>>>
/// ))]
/// 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<...>)</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<Currency.decimalPlaces,
/// Where<Currency.curyID, Equal<Current<POCreateFilter.vendorID>>>>
/// ))]
/// 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<...>)</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<Currency.decimalPlaces,
/// Where<Currency.curyID, Equal<Current<POCreateFilter.vendorID>>>>
/// ))]
/// 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。