【问题标题】:How to include a DAC extension unbound field in a Search<> parameter of PXSelector?如何在 PXSelector 的 Search<> 参数中包含 DAC 扩展未绑定字段?
【发布时间】:2020-06-28 21:10:05
【问题描述】:

我已对 INLocation.LocationCD 进行分段,并且需要限制 PXSelector 以根据输入位置的屏幕包含或排除特定分段。我已经能够访问 PXSelector 显示列中的 DAC 扩展字段,但在 PXSelector 的 Search 参数期间该字段被评估为 null。

我试过了:

  • 直接引用 INLocationExt.myField,
  • 制作继承的 DAC 以直接定义自定义字段,
  • 并创建一个 PXProjection,希望附加层会导致检索未绑定字段以填充 PXSelector,以防该字段未及时加载以进行搜索。

关键点:

  • 这是 DAC 扩展中的未绑定字段
  • 它返回一个基于评估 INLocation.LocationCD 的最后一段的值
  • 通用查询正确显示此值
  • 当我引用未绑定字段时,我无法让 PXSelector 返回任何值,除非我简单地检查 And

在我的 DAC 扩展中定义的字段:

[PXString(1)]
[PXUIField(DisplayName = "Condition")]
[ConditionType.List]
public String UsrSSCondition
{ 
    get
    {
        if (LocationCD == null || LocationCD.Length == 0) return ConditionType.Undefined;

        switch (LocationCD.Substring(LocationCD.Length - 1, 1))
        {
            case "N":
                return ConditionType.New;
            case "R":
                return ConditionType.Repair;
            case "C":
                return ConditionType.Core;
            case "U":
                return ConditionType.Used;
            default:
                return ConditionType.Undefined;
        }
    }
}
public abstract class usrSSCondition : PX.Data.BQL.BqlString.Field<usrSSCondition> { }

PXSelector:

[PXSelector(typeof(Search<INLocation.locationID, Where<INLocation.receiptsValid, Equal<True>,
    And<INLocationExt.usrSSCondition, NotEqual<ConditionType.core>>>>),
    typeof(INLocation.locationCD),
    typeof(INLocation.active),
    typeof(INLocation.primaryItemID),
    typeof(INLocation.primaryItemClassID),
    typeof(INLocationExt.usrSSCondition),
    typeof(INLocation.receiptsValid),
    SubstituteKey = typeof(INLocation.locationCD))]

PX 投影:

[Serializable]
[PXCacheName("SSCS INLocation")]
[PXProjection(typeof(Select<INLocation>))]
public partial class SSINLocation : IBqlTable
{
    #region LocationID
    [PXDBInt(IsKey = true, BqlField = typeof(INLocation.locationID))]
    public int? LocationID { get; set; }
    public abstract class locationID : PX.Data.BQL.BqlInt.Field<locationID> { }
    #endregion

    #region LocationCD
    [PXDBString(BqlField = typeof(INLocation.locationCD))]
    public String LocationCD { get; set; }
    public abstract class locationCD : PX.Data.BQL.BqlString.Field<locationCD> { }
    #endregion

    #region UsrSSCondition
    [PXDBString(BqlField = typeof(INLocationExt.usrSSCondition))]
    public String UsrSSCondition { get; set; }
    public abstract class usrSSCondition : PX.Data.BQL.BqlString.Field<usrSSCondition> { }
    #endregion

    #region ReceiptsValid
    [PXDBBool(BqlField = typeof(INLocation.receiptsValid))]
    public bool? ReceiptsValid { get; set; }
    public abstract class receiptsValid : PX.Data.BQL.BqlBool.Field<receiptsValid> { }
    #endregion

    #region Active
    [PXDBBool(BqlField = typeof(INLocation.active))]
    public bool? Active { get; set; }
    public abstract class active : PX.Data.BQL.BqlBool.Field<active> { }
    #endregion

    #region PrimaryItemID
    [PXDBInt(BqlField = typeof(INLocation.primaryItemID))]
    public int? PrimaryItemID { get; set; }
    public abstract class primaryItemID : PX.Data.BQL.BqlInt.Field<primaryItemID> { }
    #endregion

    #region PrimaryItemClassID
    [PXDBInt(BqlField = typeof(INLocation.primaryItemClassID))]
    public int? PrimaryItemClassID { get; set; }
    public abstract class primaryItemClassID : PX.Data.BQL.BqlInt.Field<primaryItemClassID> { }
    #endregion

}

我尝试了简单的版本和它们的各种组合都无济于事。如何在 PXSelector 的 Search 子句中利用我的“条件”?

编辑 1: PXSelector 作为列返回值的图片 - 不能用作 PXRestrictor 或 Select 中的 where 子句。

编辑 2:更多信息

我已简化 DAC 扩展以使用 PXFormula 并选择 PXFormula 检索到的 LocationCD 的最后一个字符来设置值。

我们需要使用 LocationCD 的最后一段来管理垃圾箱中零件的状况。这将使我们能够分开成本并管理 MRO 备件以进行维护,如新的、使用的、已修复的和需要维修的,同时还允许在需要时指定其他条件(如收到损坏的 NCM 等)。虽然某些材料可以在全球范围内使用,但某些条件下的材料需要在某些用例下可用。我的预期策略是将规则应用于位置 CD 的最后一段,以允许 PXSelector 控制用户输入,可以作为 INLocation 上的 DAC 扩展或作为相关图表中的 Cache_Attached(如有必要)。

我在 INLocation 上为 usrSSCondition 创建了一个 DAC 扩展作为 PXString。我最近的尝试是使用 PXFormula 来提取 LocationCD 值,然后在 set{} 上使用自定义代码来挑选最后一段并为相关条件设置代码。 (这种技术对我来说实际上是新的,stackoverflow 帖子中的回复引导我想到了这个想法。)

当在 PXSelector 中用作显示列时,我可以看到该值。但是, Select 不允许我进入用于显示该条件的那个段或自定义 PXString 字段。我希望一些“幕后魔术”会评估我的 PXString 字段以限制结果,但似乎该字段在 Select 期间返回为 null,然后在 DAC 处理的后续步骤中进行处理。当我考虑 Select 正在做什么时,没有存储在数据库中的数据不能用于过滤结果是有道理的。 PXRestrictor 也不对其产生影响。

1) 有没有办法让我的 DAC 在 PXSelector 应用 where 子句之前处理 PXString 值? 2)这是我需要对属性进行后处理的东西吗? (如果是这样,关于在哪里寻找一个简单的例子有什么建议吗?)

更新的 DAC:

#region usrSSCondition
private String _condition;
[PXString]
[PXUIField(DisplayName = "Condition")]
[PXFormula(typeof(INLocation.locationCD))]
[ConditionType.List]
public String UsrSSCondition
{
    get { return _condition; }
    set
    {
        string Loc = value;
        if (Loc == null || Loc.Length == 0)
        {
            _condition = ConditionType.Undefined;
        }
        else
        {
            _condition = (Loc.Substring(Loc.Length - 1, 1)) switch
            {
                "N" => ConditionType.New,
                "R" => ConditionType.Repair,
                "C" => ConditionType.Core,
                "U" => ConditionType.Used,
                _ => ConditionType.Undefined,
            };
        }
    }
}

【问题讨论】:

  • 我不确定它是否会有所帮助,但也许您可以尝试在 Unbound 字段中使用 PXFormula,而不是在 getter 中使用逻辑。也许 Acumatica 可能会在您的搜索中包含公式
  • 感谢您的回复。我考虑过这一点,但该领域的重点是隔离细分市场。你知道在 PXFormula 中做到这一点的方法吗?正如您从显式 get 块中看到的那样,我从位置中删除最后一个字符以返回由段标识的条件。如果有办法在开关中做到这一点,我可以使用asiablog.acumatica.com/2016/08/cool-ways-to-use-pxformula.html 中的示例来执行您的建议。我只是不知道如何在该语法中从 LocationCD 中获得最后一个位置......因此我在上面剥离它的方式。有什么想法吗?
  • 可能无法在 PXFormula 中完成所有这些操作。由于我们不确定 PXFormula 是否可以工作……您是否可以做一个简单的测试,在其中添加 PXFormula 并且您现在只返回其中一个值,例如 ConditionType.Repair。如果可行,也许我们可以尝试找到一种方法让 PXFormula 与子字符串逻辑一起工作
  • 我在做一些根本错误的事情,但我没有看到它。我可以在选择器列中返回条件(将图像添加到问题中),但我似乎无法在 Search 或 PXRestrictor 中使用它。尝试 PXFormula 没有用,我什至尝试包含 PXDependsOnFields 来尝试轻推它。会睡在上面,早上再试一次。 (如果我有任何地方,请更新)
  • 与 Huges Beauséjour 的一些离线讨论证实了我的猜测......“过滤发生在数据库级别,在数据库中未绑定字段不存在”。他的建议是使用直接在 PXSelector 或 PXRestrictor 中从 locationCD 字段评估段的方法,并使用 switch/case。有点复杂,需要维护,但到目前为止建议的唯一方法是完成我的问题。

标签: acumatica


【解决方案1】:

永远不要在 getter 中使用代码,它不会在 BQL 表达式中正常工作!

如果你想在 BQL 中的某个地方检查 Loc.Substring(Loc.Length - 1, 1),只需编写你自己的 BQL 函数

public class ConditionTypeBySegment<Source> : BqlFunction, IBqlOperand, IBqlCreator
    where Source : IBqlOperand
{
    private IBqlCreator _source;

    public void Verify(PXCache cache, object item, List<object> pars, ref bool? result, ref object value)
    {
        if (!getValue<Source>(ref _source, cache, item, pars, ref result, out value) || value == null)
            return;

        if (value is string strValue)
        {
            switch (strValue.Substring(strValue.Length - 1, 1))
            {
                case "N":
                    value = ConditionType.New;
                    break;
                case "R":
                    value = ConditionType.Repair;
                    break;
                case "C":
                    value = ConditionType.Core;
                    break;
                case "U":
                    value = ConditionType.Used;
                    break;
                default:
                    value = ConditionType.Undefined;
                    break;
            }

            return;
        }

        value = ConditionType.Undefined;
    }

    public bool AppendExpression(ref SQLExpression exp, PXGraph graph, BqlCommandInfo info, BqlCommand.Selection selection)
    {
        ...
        return true;
    }
}

或使用现有功能的组合。例如:

[PXSelector(typeof(Search<INLocation.locationID,
    Where<INLocation.receiptsValid, Equal<True>,
        And<Substring<FABookBalance.deprToPeriod, Sub<StrLen<FABookBalance.deprToPeriod>, int1>, int1>, NotEqual<ConditionTypes.tCore>>>>),
    typeof(INLocation.locationCD),
    typeof(INLocation.active),
    typeof(INLocation.primaryItemID),
    typeof(INLocation.primaryItemClassID),
    typeof(INLocationExt.usrSSCondition),
    typeof(INLocation.receiptsValid),
    SubstituteKey = typeof(INLocation.locationCD))]

public static class ConditionTypes
{
    public class tNew : PX.Data.BQL.BqlString.Constant<tNew> { public tNew() : base("N") { } }
    public class tRepair : PX.Data.BQL.BqlString.Constant<tRepair> { public tRepair() : base("R") { } }
    public class tCore : PX.Data.BQL.BqlString.Constant<tCore> { public tCore() : base("C") { } }
    public class tUsed : PX.Data.BQL.BqlString.Constant<tUsed> { public tUsed() : base("U") { } }
}

【讨论】:

    【解决方案2】:

    简单的解决方案 - 简化。将字段更改为仅保存该值,然后在设置 locaitonCD 值时让 BLC 设置该值。在创建记录时,locationCD 字段为空,因此定义 FieldDefaulting 逻辑会导致最初未定义条件。通过监控 LocationCD 的 FieldUpdated,我们可以将 FieldDefaulting 规则重新应用到“真实”值。

    DAC 字段定义:

    #region usrSSCondition
    [PXDBString]
    [PXUIField(DisplayName = "Condition")]
    [ConditionType.List]
    public String UsrSSCondition { get; set; }
    public abstract class usrSSCondition : PX.Data.BQL.BqlString.Field<usrSSCondition> { }
    #endregion
    

    BLC 中的事件处理程序:

    #region INLocationExt_UsrSSCondition_FieldDefaulting
    protected void INLocation_UsrSSCondition_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
    {
        INLocation row = (INLocation)e.Row;
    
        string Loc = row?.LocationCD;
        if (Loc == null || Loc.Length == 0)
        {
            e.NewValue = ConditionType.Undefined;
        }
        else
        {
            e.NewValue = (Loc.Substring(Loc.Length - 1, 1)) switch
            {
                ConditionType.New => ConditionType.New,
                ConditionType.Repair => ConditionType.Repair,
                ConditionType.Core => ConditionType.Core,
                ConditionType.Used => ConditionType.Used,
                _ => ConditionType.Undefined,
            };
        }
    
    }
    #endregion
    
    #region INLocation_LocationCD_FieldUpdated
    protected void _(Events.FieldUpdated<INLocation.locationCD> e)
    {
        INLocation row = (INLocation)e.Row;
        e.Cache.SetDefaultExt<INLocationExt.usrSSCondition>(row);
    }
    #endregion
    

    由于位置是在 INSiteMaint 中定义的,因此该图中的事件处理程序允许将字段值设置为存储在数据库中而无需任何转换。这允许使用 PXRestrictorAttribute 来相应地限制可用位置或编写规则以在 INSiteMaint 屏幕上设置位置标志。

    以下是 CacheAttached 的一个示例,用于添加 PXRestrictor 以防止接收到核心位置类型,除非它是从 NcmTag 屏幕完成的。 (并非普遍需要严格控制用户可能选择的位置,因此这并未全局应用于 DAC 领域。)

    #region INTran_LocationID_CachedAttached
    [PXMergeAttributes(Method = MergeMethod.Append)]
    [PXRestrictor(typeof(Where<INLocationExt.usrSSCondition, NotEqual<ConditionType.core>,
         Or<Current<AccessInfo.screenID>, Equal<SSCS.Constants.NcmTagScreenID>>>), "")]
    #endregion
    

    另外值得注意的是,由于我的目的是使用 LocationCD 的字符让最终用户识别位置类型,因此我必须防止用户通过 INLocation 的 RowSelected 事件更改 LocationCD 值。

    #region INLocation_RowSelected
    protected void _(Events.RowSelected<INLocation> e)
    {
        INLocation row = e.Row;
        if(row?.SiteID != null)
        {
            INLocationExt rowExt = row.GetExtension<INLocationExt>();
            PXUIFieldAttribute.SetEnabled<INLocation.locationCD>(e.Cache, row, 
                !DisableLocationRename(row?.LocationID));
        }
    }
    #endregion
    
    #region DisableLocationRename
    protected virtual bool DisableLocationRename(int? locationID)
    {
        int counter = PXSelect<SSINNcmTag,
            Where<SSINNcmTag.locationID, Equal<Required<SSINNcmTag.locationID>>,
              And<SSINNcmTag.tranRefNbr, IsNull>>>
            .SelectSingleBound(Base, null, locationID).Count;
        if (counter > 0) return true;
        counter = PXSelect<INLocationStatus,
            Where <INLocationStatus.locationID, Equal<Required<INLocationStatus.locationID>>,
              And<INLocationStatus.qtyOnHand, Greater<DecimalZero>>>>
            .SelectSingleBound(Base, null, locationID).Count;
        if (counter > 0) return true;
        return false;
    }
    #endregion
    

    虽然我们有能力编写一些非常有趣的代码,但重要的是不时停下来问一句:“我为什么要让这件事变得复杂?”尽可能简化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-14
      • 2012-09-12
      • 2019-03-16
      • 2018-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-09
      相关资源
      最近更新 更多