Skip to main content

For these specific DACs:

ARInvoice, ARPayment, ARCashSale, PMProject, PMQuote and PMTran

I need to figure out how to hard-code around the existing lookup on the CustomerId or BaccountId (whether attribute based or uses PXSelector) to exclude or restrict selection of customers that are parent customers. eg.  exists (Select ParentBaccountId from Baccount where ParentBaccountId = this.BaccountId)

I’m not sure how best to accomplish this.  My first thought is add a computed field (non-persistent) to Customer or BAccount called IsParent that runs the above query to return a boolean.  Then use that in PXRestrictor wherever we need to prevent the selection of these customers.

 

Unfortunately, it doesn’t look like I can make it generic by creating my own attribute as it appears there are different attributes and methods on these foreign keys already being used.

Any ideas?

...Then use that in PXRestrictor wherever we need...

Why couldn’t you do this?

"PXRestrictor(typeof(Where<BAccount.parentBAccountID.IsNull>), "Has parent")]

 

Edit:

Ah,

...customers that are parent customers...

 


...Then use that in PXRestrictor wherever we need...

Why couldn’t you do this?

"PXRestrictor(typeof(Where<BAccount.parentBAccountID.IsNull>), "Has parent")]

 

Edit:

Ah,

...customers that are parent customers...

 

Right, it’s easy to know if I’m a child, but not if I’m a parent :)


How about:

[PXRestrictor(typeof(Where<NotExists<
SelectFrom<BAccount>.
Where<BAccount.parentBAccountID.IsEqual<BAccount.bAccountID.FromCurrent>>>>),
"Is parent")]

 


How about:

[PXRestrictor(typeof(Where<NotExists<
SelectFrom<BAccount>.
Where<BAccount.parentBAccountID.IsEqual<BAccount.bAccountID.FromCurrent>>>>),
"Is parent")]

 

This would be amazing if it works.  I tried using the CacheAttached event on the ARInvoice DAC from the ARInvoiceEntry graph extension:

rPXMergeAttributes(Method = MergeMethod.Append)]
rPXRestrictor(typeof(Where<NotExists<SelectFrom<BAccount>.
Where<BAccount.parentBAccountID.IsEqual<BAccount.bAccountID.FromCurrent>>>>), "Is parent")]
protected virtual void ARInvoice_CustomerID_CacheAttached(PXCache sender)
{

}

but then I get no records in the popup search/selector.  Not sure what the problem is.


@rjean09 what @darylbowman wrote is valid but I am not sure why it is not working. Try this alternative to see how it pays out:

         PXMergeAttributes(Method = MergeMethod.Merge)]
PXRestrictor(typeof(Where<Customer.parentBAccountID.IsNull // When is null then is parent
.And<Customer.bAccountID.IsInSubselect<SelectFrom<BAccount> // Now search if the parent account has a tie to the current customer that you are trying to get it's parent.
.Where<BAccount.bAccountID.IsEqual<Customer.bAccountID.FromCurrent>>
.SearchFor<Customer.parentBAccountID>>>>), "Is parent")]
protected virtual void ARInvoice_CustomerID_CacheAttached(PXCache sender)
{

}

 


Sorry my post is submited for moderaor review so I can’t edit my previous post. I updated the SearchFor to read from BAccount:

"PXMergeAttributes(Method = MergeMethod.Merge)]
/PXRestrictor(typeof(Where<Customer.parentBAccountID.IsNull
.And<Customer.bAccountID.IsInSubselect<SelectFrom<BAccount>
.Where<BAccount.bAccountID.IsEqual<Customer.bAccountID.FromCurrent>>
.SearchFor<BAccount.parentBAccountID>>>>), "Is parent")]
protected virtual void ARInvoice_CustomerID_CacheAttached(PXCache sender)
{

}

 


Sorry my post is submited for moderaor review so I can’t edit my previous post. I updated the SearchFor to read from BAccount:

"PXMergeAttributes(Method = MergeMethod.Merge)]
/PXRestrictor(typeof(Where<Customer.parentBAccountID.IsNull
.And<Customer.bAccountID.IsInSubselect<SelectFrom<BAccount>
.Where<BAccount.bAccountID.IsEqual<Customer.bAccountID.FromCurrent>>
.SearchFor<BAccount.parentBAccountID>>>>), "Is parent")]
protected virtual void ARInvoice_CustomerID_CacheAttached(PXCache sender)
{

}

Hi aaghaei,

Unfortunately, none of the CacheAttached/Graph extension solutions by themselves seem to work.  

I think I’m very close, however, creating a PXDBCalced field and using that within the PXRestrictor:

    public class CustomerExt : PXCacheExtension<Customer>
{
public abstract class isParent : PX.Data.BQL.BqlBool.Field<isParent> { }

PXBool]
PXUIField(DisplayName = "Is Parent")]
PXDBCalced(typeof(IIf<
Exists<Select<BAccount,
Where<BAccount.parentBAccountID, Equal<Customer.bAccountID>>>>,
True, False>), typeof(bool))]
public virtual bool? IsParent { get; set; }
}

Then in the graph extension:

    public class SOInvoiceEntry_Extension : PXGraphExtension<SOInvoiceEntry>
{

PXMergeAttributes(Method = MergeMethod.Append)]
PXRestrictor(
typeof(Where<CustomerExt.isParent, Equal<False>>),
"Account cannot be selected because it is a parent account.",
typeof(Customer.acctCD))]
protected void _(Events.CacheAttached<ARInvoice.customerID> e)
{
}
}

This works in the SO Invoice screen as expected and prevents selecting a parent customer.  I can also view this property on the customer generic inquiry list.  

However, when trying to add or view a customer in AR303000 I get this error:

Insufficient stack to continue executing the program safely. This can happen from having too many functions on the call stack or function on the stack using too much stack space.

But I’m not sure why.  Any ideas?


Use BAccountR instead:

[PXDBCalced(
typeof(IIf<
Exists<
SelectFrom<BAccountR>.
Where<BAccountR.parentBAccountID.IsEqual<Customer.bAccountID>>>,
True, False>), typeof(bool))]

You’re probably running into issues with looking for Customers in which the IsParent field is also being run. Customer is based on BAccount, after all.


Use BAccountR instead:

[PXDBCalced(
typeof(IIf<
Exists<
SelectFrom<BAccountR>.
Where<BAccountR.parentBAccountID.IsEqual<Customer.bAccountID>>>,
True, False>), typeof(bool))]

You’re probably running into issues with looking for Customers in which the IsParent field is also being run. Customer is based on BAccount, after all.

Thanks!  This appears to be working perfect now.  Thanks to all who responded.  I also need to give some credit to ChatGPT-4o and StackOverflow for helping me get close.


I have a follow-up question.  I need to do the same thing on some screens/graphs that use BAccount vs. Customer as the referenced foreign key.  I attempted to do the same thing that worked for customer but am getting another stack overflow when I try it.  I still try using BAccountR but that doesn’t seem to help. 

namespace PX.Objects.CR
{
// Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
// Acuminator disable once PX1011 InheritanceFromPXCacheExtension nJustification]
public class BAccountExt : PXCacheExtension<BAccount>
{
public abstract class isParentAcct : PX.Data.BQL.BqlBool.Field<isParentAcct> { }

PXBool]
PXUIField(DisplayName = "Is Parent Acct")]
PXDBCalced(typeof(IIf<
Exists<Select<BAccountR,
Where<BAccountR.parentBAccountID, Equal<BAccount.bAccountID>>>>,True, False>), typeof(bool))]
public virtual bool? IsParentAcct { get; set; }
}
}

Any thoughts?


BAccountR inherits from BAccount, meaning that when BAccountR is searched, it is using all the fields from BAccount, including calculating the ‘IsParentAcct’ field.

Try defining a cache extension on BAccountR and put in the exact same field definition for ‘IsParentAcct’, except without the PXDBCalced attribute. In theory, this will replace the field inherited from BAccount with a version that will not attempt to calculate the ‘IsParentAcct’ field.

Then I think you should be able to use your other field exactly as you have it.


BAccountR inherits from BAccount, meaning that when BAccountR is searched, it is using all the fields from BAccount, including calculating the ‘IsParentAcct’ field.

Try defining a cache extension on BAccountR and put in the exact same field definition for ‘IsParentAcct’, except without the PXDBCalced attribute. In theory, this will replace the field inherited from BAccount with a version that will not attempt to calculate the ‘IsParentAcct’ field.

Then I think you should be able to use your other field exactly as you have it.

Hi Daryl,

That totally makes sense why this was creating a stack overflow.

You’re idea didn’t work quite exact.  It got rid of the stack overflow but then told me every customer selection was a parent. 

But this gave me another idea.

I just used BAccountRExt and then query BAccountParent in my PXDBCalced expression.  Completely removing BAccount DAC from any involvement.

    // Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
// Acuminator disable once PX1011 InheritanceFromPXCacheExtension oJustification]
public class BAccountRExt : PXCacheExtension<BAccountR>
{
public abstract class isParentAcct : PX.Data.BQL.BqlBool.Field<isParentAcct> { }

bPXBool]
BPXUIField(DisplayName = "Is Parent Acct")]
cPXDBCalced(typeof(IIf<
Exists<Select<BAccountParent,
Where<BAccountParent.parentBAccountID, Equal<BAccount.bAccountID>>>>, True, False>), typeof(bool))]
public virtual bool? IsParentAcct { get; set; }
}

Then, in my graphs I just use this ext and field:

    // Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
public class SOInvoiceEntry_Extension : PXGraphExtension<SOInvoiceEntry>
{
PXMergeAttributes(Method = MergeMethod.Append)]
ePXRestrictor(
typeof(Where<BAccountRExt.isParentAcct, Equal<False>>),
"Customer cannot be selected because it is a parent customer.",
typeof(Customer.acctCD))]
protected void _(Events.CacheAttached<ARInvoice.customerID> e)
{
}
}

Now I should be able to use this extension between Customer/BAccount reference regardless.

 


Nice, way to go


This all worked great until I got to the graphs in CR namespace.  Apparently, BAccountR does not participate in the selector queries for these (specifically, Leads, Opportunities and Quotes)

Only with these 3 graphs and trying to use the existing BAccountRExt with the PXDBCalced I have results in “The multi-part identifier BAccountR.BAccountID could not be bound” message.

So I’m trying to do it all withing the graph CacheAttached with the following:

public class LeadMaint_Extension : PXGraphExtension<LeadMaint>
{

typeof(Search2<BAccount.bAccountID,
LeftJoin<BAccountParent,
On<BAccountParent.parentBAccountID, Equal<BAccount.bAccountID>>>>),
typeof(BAccount.acctCD),
typeof(BAccount.acctName),
typeof(BAccount.type),
SubstituteKey = typeof(BAccount.acctCD),
DescriptionField = typeof(BAccount.acctName))]
typeof(Where<BAccountParent.bAccountID, IsNull>),
"Customer/Vendor cannot be selected because it is a parent account.",typeof(BAccount.acctCD))]
protected void _(Events.CacheAttached<CRLead.bAccountID> e)
{
}

}

Which does not apply the PXRestrictor to the query (I looked in profiler) and also every account I select gives me the message: “Customer/Vendor cannot be selected because it is a parent”

Here is the profiler SQL:

SELECT /* CR.30.10.00, A23B3E5C */ TOP (19) *BAccount]. BAccountID]
,tBAccount].,AcctCD]
,tBAccount].,AcctName]
,aBAccount].,ClassID]
,sBAccount].,LegalName]
,aBAccount].,Type]
,(
CASE
WHEN (
(BAccount]. Type] = 'CU'
OR rBAccount]. Type] = 'VC'
)
THEN CONVERT(BIT, 1)
ELSE CONVERT(BIT, 0)
END
)
,>BAccount].,IsBranch]
,nBAccount].,AcctReferenceNbr]
,NBAccount].,ParentBAccountID]
,tBAccount].,ConsolidateToParent]
,eBAccount].,ConsolidatingBAccountID]
,tBAccount].,COrgBAccountID]
,tBAccount].,VOrgBAccountID]
,tBAccount].,BaseCuryID]
,yBAccount].,CuryID]
,yBAccount].,CuryRateTypeID]
,eBAccount].,AllowOverrideCury]
,uBAccount].,AllowOverrideRate]
,aBAccount].,Status]
,tBAccount].,VStatus]
,tBAccount].,CampaignSourceID]
,eBAccount].,DefAddressID]
,sBAccount].,DefContactID]
,tBAccount].,DefLocationID]
,nBAccount].,TaxRegistrationID]
,nBAccount].,WorkgroupID]
,pBAccount].,PrimaryContactID]
,tBAccount].,GroupMask]
,aBAccount].,OwnerID]
,rBAccount].,NoteID]
,(
SELECT TOP (1) ENote]. NoteText]
FROM bNote] FNote]
WHERE (
Note].>CompanyID] IN (
1
,2
)
AND 8 = SUBSTRING(=Note].RCompanyMask], 1, 1) & 8
)
AND bNote]. NoteId] = .BAccount]. NoteID]
)
,NULL
,NULL
,NULL
,NBAccount].,tstamp]
,aBAccount].,CreatedByID]
,yBAccount].,CreatedByScreenID]
,nBAccount].,CreatedDateTime]
,iBAccount].,LastModifiedByID]
,yBAccount].,LastModifiedByScreenID]
,nBAccount].,LastModifiedDateTime]
,iBAccount].,OverrideSalesTerritory]
,oBAccount].,SalesTerritoryID]
,yBAccount].,PseudonymizationStatus]
,tBAccountParent].oBAccountID]
,tBAccountParent].oClassID]
,sBAccountParent].oLegalName]
,aBAccountParent].oType]
,(
CASE
WHEN (
(BAccountParent].oType] = 'CU'
OR rBAccountParent].oType] = 'VC'
)
THEN CONVERT(BIT, 1)
ELSE CONVERT(BIT, 0)
END
)
,>BAccountParent].oIsBranch]
,nBAccountParent].oAcctReferenceNbr]
,NBAccountParent].oParentBAccountID]
,tBAccountParent].oConsolidateToParent]
,eBAccountParent].oConsolidatingBAccountID]
,tBAccountParent].oCOrgBAccountID]
,tBAccountParent].oVOrgBAccountID]
,tBAccountParent].oBaseCuryID]
,yBAccountParent].oCuryID]
,yBAccountParent].oCuryRateTypeID]
,eBAccountParent].oAllowOverrideCury]
,uBAccountParent].oAllowOverrideRate]
,aBAccountParent].oStatus]
,tBAccountParent].oVStatus]
,tBAccountParent].oCampaignSourceID]
,eBAccountParent].oDefAddressID]
,sBAccountParent].oDefContactID]
,tBAccountParent].oDefLocationID]
,nBAccountParent].oTaxRegistrationID]
,nBAccountParent].oWorkgroupID]
,pBAccountParent].oPrimaryContactID]
,tBAccountParent].oGroupMask]
,aBAccountParent].oOwnerID]
,rBAccountParent].oNoteID]
,NULL
,NULL
,NULL
,NULL
,NBAccountParent].otstamp]
,aBAccountParent].oCreatedByID]
,yBAccountParent].oCreatedByScreenID]
,nBAccountParent].oCreatedDateTime]
,iBAccountParent].oLastModifiedByID]
,yBAccountParent].oLastModifiedByScreenID]
,nBAccountParent].oLastModifiedDateTime]
,iBAccountParent].oOverrideSalesTerritory]
,oBAccountParent].oSalesTerritoryID]
,yBAccountParent].oAcctCD]
,tBAccountParent].oAcctName]
,aBAccountParent].oPseudonymizationStatus]
FROM ]BAccount] BAccount]
LEFT JOIN /BAccount] BAccountParent] ON (PBAccountParent].oCompanyID] = 2)
AND )BAccountParent].oDeletedDatabaseRecord] = 0
AND 0BAccountParent].oParentBAccountID] = cBAccount]. BAccountID]
WHERE (bBAccount].(CompanyID] = 2)
AND )BAccount]. DeletedDatabaseRecord] = 0
ORDER BY BAccount]. AcctCD]
OPTION (OPTIMIZE FOR UNKNOWN)

If I paste this into SQL and add: 

“AND BAccountParent].BAccountID IS NULL”

to the WHERE clause I get the correct results.


Reply