如何在 PXSelector 的 Search<> 参数中包含 DAC 扩展未绑定字段?

How to include a DAC extension unbound field in a Search<> parameter of PXSelector?

我已经分段 INLocation.LocationCD 并且需要限制 PXSelect 或根据输入位置的屏幕包括或排除特定分段。我已经能够访问 PXSelector 的显示列中的 DAC 扩展字段,但是在 PXSelector.[=15= 的 Search<> 参数期间该字段被评估为空]

我试过:

要点:

我的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> { }

PXSelect或:

[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))]

PXProjection:

[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<> 子句中利用我的 "condition"

编辑 1: PXSelect 或 return 值作为列的图片 - 不能用作 PXRestrictor 或 Select<>.

中的 where 子句

编辑 2:更多信息

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

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

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

在 PXSelect 中使用或用作显示列时,我可以看到该值。但是,Select<> 不允许我进入该段或用于显示该条件的自定义 PXString 字段。我希望某些 "behind the scenes magic" 会评估我的 PXString 字段以限制结果,但似乎该字段在 Select 期间被 return 编辑为空,然后在后面的步骤中处理DAC处理。当我考虑 Select 正在做什么时,不存储在数据库中的数据不能用于过滤结果是有道理的。 PXRestrictor 也不影响它。

1) 有没有办法让我的 DAC 在 PXSelect 或应用 where 子句之前处理 PXString 值? 2) 这是我需要对 post 处理的属性采取的措施吗? (如果是这样,关于在哪里寻找简单示例有什么建议吗?)

更新的 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,
            };
        }
    }
}

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

如果您想检查 Loc.Substring(Loc.Length - 1, 1) BQL 中的某处,只需编写您自己的 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") { } }
}

简单的解决方案 - 简化。将字段更改为简单地保存该值,然后在设置 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 屏幕完成的。 (控制用户可能 select 的位置的重手并不是普遍需要的,因此这并未在全球范围内应用于 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

虽然我们有能力编写一些非常有趣的代码,但时不时停下来问问“我为什么要把它弄得这么复杂”是很重要的。尽可能简化。