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?
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.
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.