BQL expression in FieldDefaulting for Current User - acumatica

I'm trying to set the default value of a custom field to the defContactID of the currently logged in user. I cannot get the BQL correct, any advise?
namespace PX.Objects.PJ.DailyFieldReports.PJ.Graphs
{
public class DailyFieldReportEntry_Extension : PXGraphExtension<DailyFieldReportEntry>
{
protected void DailyFieldReport_UsrOwner_FieldDefaulting(PXCache cache, PXFieldDefaultingEventArgs e, PXFieldDefaulting InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = e.Row as DailyFieldReport;
//BQL Statement to pull the EPEmployee record for the currently logged in employee
e.NewValue = EPEmployee.defContactID;
}
}
}

To get the logged in user details you can use the AccessInfo object which is in every graph. Accessinfo.UserContactID will retrieve the logged in user contact ID.
you can also use this by specifying it in the PXDefault attribute, without having the need to use the field event. In this case:
[PXDefault(typeof(AccessInfo.userContactID))

Related

While deleting the customer, how can i set null value for the custom field for the assigned contacts in Acumatica

I have tried multiple ways, but getting Another process error in the default version of Acumatica 19.106.0020
On top of it i have a customized code on both customer and contact screen, my requirement to clear the value of the custom field that is created in contact table when customer is deleting from the screen AR303000 i need to set null value of the custom field for the deleted contact from the customer.
i have tried by setting value on Customer_RowDeleting event but continuously getting Another process error, below is the screenshot error
Below is the code that i was tried
protected virtual void Customer_RowDeleting(PXCache sender, PXRowDeletingEventArgs e, PXRowDeleting BaseEvent)
{
BaseEvent?.Invoke(sender, e);
Customer rows = e.Row as Customer;
if (rows == null)
return;
if (Base.BAccount.Cache.GetStatus(Base.BAccount.Current) == PXEntryStatus.Deleted)
{
foreach (Contact BACT in PXSelectReadonly<Contact,
Where<Contact.bAccountID, Equal<Required<Contact.bAccountID>>,
And<Contact.contactType, NotEqual<ContactTypesAttribute.bAccountProperty>>>>.Select(Base, rows.BAccountID))
{
ContactMaint congraph = PXGraph.CreateInstance<ContactMaint>();
Contact CTData = PXSelectReadonly<Contact,
Where<Contact.contactID, Equal<Required<Contact.contactID>>>>.Select(Base, BACT.ContactID);
if (CTData != null)
{
congraph.Contact.Current = CTData;
if (congraph.Contact.Current != null)
{
congraph.Contact.SetValueExt<ContactExt.usrKWBAccountId>(congraph.Contact.Current, null);
congraph.Contact.Update(congraph.Contact.Current);
congraph.Save.Press();
}
}
}
}
}
Thanks in advance.
Hi Chris, please find the attached image here
I don't recommend to create graphs during RowDeleting event. If you have Acuminator installed, you will see a warning about creating graphs in event handlers.
Instead, call your custom code during the Persist method. Persist method is called during Delete operation. After the Base persist is finished, your custom code can perform it's work. Try something like this
public class CustomerMaint_Extension : PXGraphExtension<CustomerMaint>
{
public delegate void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
Customer currentCustomer = Base.CurrentCustomer.Current; //get the customer record before it's deleted, i.e. Customer.bAccountID
baseMethod(); //let the base delete process happen first
if (Base.CurrentCustomer.Cache.GetStatus(currentCustomer) == PXEntryStatus.Deleted)
{
using (PXTransactionScope ts = new PXTransactionScope())
{
//here is where you add your code to delete other records
ts.Complete(); //be sure to complete the transaction scope
}
}
}
}
Also you might want to unpublish other customization packages, and see if the error continues without those packages. That is one way to determine the source of the error...by process of elimination.

I need to conditionally set a field to be required

I'm customizing the Sales Order screen as follows:
I've added a custom boolean field added to the Order Types screen called 'Require Customer Order Number'.
I've added code to the BLC of the Sales Order screen where I want to conditionally make the CustomerOrderNumber field required, based on whether the 'Require Customer Order Number' field is checked or not.
I'm using the SOOrder_RowSelected event as follows:
protected virtual void SOOrder_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
var soorder = (SOOrder)e.Row;
if (soorder == null) return;
string ordtype = soorder.OrderType;
var soot = (SOOrderType)PXSelect<SOOrderType,
Where<SOOrderType.orderType, Equal<Required<SOOrderType.orderType>>>>.Select(Base, ordtype);
if (soot != null)
{
var sootext = PXCache<SOOrderType>.GetExtension<SOOrderTypeExt>(soot);
if (sootext != null)
{
PXUIFieldAttribute.SetRequired<SOOrder.customerOrderNbr>(sender, sootext.UsrRequireCustOrdNbr == null ? false : (bool)sootext.UsrRequireCustOrdNbr);
}
}
}
This DOES put an asterisk on the CustomerOrderNumber field - but it doesn't spawn an error upon save if that field is empty.
Another issue is my PXSelect to get the record out of SOOrderType ALWAYS returns a null for the check box user field, even if it has a 'True' value in the database (which is why I put the ternary operator on the call). Even if I hard-code a 'true' value in the PXUIFieldAttribute.SetRequired call, it still doesn't spawn the error to prevent a save. The asterisk is there, but it doesn't work.
If I use a Cache_Attached event to add [PXDefault] it works perfectly - but this doesn't help me - I need it conditionally set.
Any ideas?
Required is used only for displaying the asterisk. PXDefault attribute is the one that makes the field mandatory based on PersistingCheck property value.
The issue is that PXUIFieldAttributes like PersistingCheck can only be set once at the time of graph creation. You can set it dynamically in the constructor/Initialize method but if you change the property after that it has no effects.
When I need a field to be mandatory based on a dynamic condition I remove the PXDefault attribute and validate the field manually in event handlers like RowPersisting:
public void PMTimeActivity_RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
{
PMTimeActivity timeActivity = e.Row as PMTimeActivity;
if (timeActivity != null && PMTimeActivity.timeSpent == null)
{
PXUIFieldAttribute.SetError<PMTimeActivity.timeSpent>(sender, timeActivity, "'Time Spent' cannot be empty."));
}
}

How to Count records in a related table and allow user to override

I have a custom field on the CRActivity table in which I need to store the number of records in a related table. I am trying to set the field to the value when screen CR306030 opens. The user needs to be able to override the calculated number so, I'm thinking that I need logic on the calculation to check if the custom field is > 0, in which case, don't populate the custom field and assume it's already been set.
Previously, I've tried to do this in the Field_Selecting events but, this is not working. I'm thinking I might be able to use a PXFormula attribute. Any suggestions?
I tried making a custom attribute which is close but, it won't save the values to the db. The save button enables, I can click it and it looks like it saves but, no dice. Some mundane detail, I'm sure.....
Here's my custom attribute:
public class CFCountIfZeroAttribute : PXIntAttribute
{
public override void FieldSelecting(PXCache cache, PXFieldSelectingEventArgs e)
{
if (e.Row == null)
return;
CRActivity activity = (CRActivity)e.Row;
CRActivityExt activityExt = activity.GetExtension<CRActivityExt>();
if (activityExt.usrCustomField <= 0)
{
int aggregateValue = BQLToFind();
e.ReturnValue = aggregateValue;
cache.SetValue<CRActivityExt.usrCustomField>(e.Row, aggregateValue);
cache.IsDirty = true;
}
}
}
Thanks!
I don't think I've ever done a count within a PXFormula, but what if you created a custom attribute?
[DBUIInt()]
[EditableCount()]
public virtual int? CountField
public class EditableCountAttribute
{
public override void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
if (e.Row != null)
{
//if CountField is 0, lookup the count from another table
}
}
}
This is just off the top of my head. You could pass the field you're counting into the attribute if you wanted to do this elsewhere.

BQL on custom DAC referencing SOOrder.orderNbr not reflecting current Order Nbr

We have a test DAC called UsrNonRelatedScanField with two fields : OrderNbr and ScanStatus.
Here's our simple query to grab the correct ordernbr and assign it to a SOOrderExt field:
NonRelatedScanField lastScan = PXSelect<NonRelatedScanField,
Where<NonRelatedScanField.orderNbr,
Equal<Required<SOOrder.orderNbr>>>>.Select(Base, row.OrderNbr);
if(lastScan != null)
{
rowExt.UsrNonRelatedScanField = lastScan.ScanStatus;
}
This logic is held in a SOOrder_RowSelecting() method.
Full Method implementation:
protected virtual void SOOrder_RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
{
SOOrder row = (SOOrder)e.Row;
if (row == null) return;
SOOrderExt rowExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(row);
NonRelatedScanField lastScan = PXSelect<NonRelatedScanField,
Where<NonRelatedScanField.orderNbr,
Equal<Required<SOOrder.orderNbr>>>>.Select(Base, row.OrderNbr);
if (lastScan != null)
{
rowExt.UsrNonRelatedScanField = lastScan.ScanStatus;
}
}
Expected Results : Get the current Orders scan status from lastScan DAC
Actual Results: Will populate correctly only on the initial order opened. When selecting other orders the old value is persisting unless I manually refresh the page. When manually refreshed the correct data comes in.
I haven't had any issues in the past with BQL queries, this specific query is not behaving as expected.
Thank you

How to assign Invoice RefNbr before saving the invoice

I have set my invoice to Manual Numbering
And I want to assign the invoice number(RefNbr) - if blank, before saving the invoice (AR301000). I've overridden the RowPersisting event as follow:
public class ARInvoiceEntry_Extension:PXGraphExtension<ARInvoiceEntry>
{
protected void ARInvoice_RowPersisting(PXCache cache, PXRowPersistingEventArgs e, PXRowPersisting InvokeBaseHandler)
{
var row = (ARInvoice)e.Row;
if (row != null)
{
//BB-<timestamp> as inv# for testing only
if (string.IsNullOrEmpty(row.RefNbr))
row.RefNbr = "BB-" + DateTime.Now.ToString("hhmmsstt");
}
if(InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
}
}
If I raised a new invoice w/out any lines and saved it. It correctly assigned the Reference Nbr as expected.
Problem is if I raised a new invoice WITH lines. Its complaining that the RefNbr is blank.
What am I missing ? How do I fix this ? Thanks.
Acumatica's PXDBDefaultAttribute only updates field of the dependent records when both the source field of the parent record and the foreign key field of the dependent record are not empty.
When a dependent record is inserted into the cache, PXDBDefaultAttribute will copy the source field value from the parent record to the field it decorates from the dependent record. While the framework is saving new parent record into the database, it first raises RowPersisting event for the parent record. PXDBDefaultAttribute subscribes to the RowPersisting event of the parent record type to save the original source field value:
to locate the parent record after it was saved to the database
or to use it as a restore point for the dependent field in case of an aborted transaction
Also, PXDBDefaultAttribute subscribes to the RowPersisting event of the dependent record type to update the foreign key field with the actual source field value, that has just been recorded in the database. To update the foreign key field PXDBDefaultAttribute must first locate the parent record using the original source field value obtained earlier in the RowPersisting event handler of the parent record type. In case the foreign key field value appears to be empty, there is no chance for PXDBDefaultAttribute to locate the parent record for it and it simply leaves the dependent field empty. This is what eventually is causing the error "RefNbr cannot be empty".
With all that being said, I believe it won't be possible to achieve the desired results if leaving AR Invoice Reference Number empty until it gets saved to the database. As an alternative, let me suggest to default AR Invoice Reference Number to some constant, like < ENTER >, and at the same time replace it with the actual number within the ARInvoice_RowPersisting handler:
using PX.Data;
using System;
namespace PX.Objects.AR
{
public class ARInvoiceNumberingCstAttribute : ARInvoiceType.NumberingAttribute
{
public const string EnterRefNbr = "<ENTER>";
protected override string GetNewNumber()
{
string newNumber = base.GetNewNumber();
if (string.IsNullOrEmpty(newNumber))
{
newNumber = EnterRefNbr;
}
return newNumber;
}
public override void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
{
if (GetNewNumber() == EnterRefNbr) return;
base.RowPersisting(sender, e);
}
}
public class ARInvoiceEntry_Extension : PXGraphExtension<ARInvoiceEntry>
{
[PXRemoveBaseAttribute(typeof(ARInvoiceType.NumberingAttribute))]
[PXMergeAttributes(Method = MergeMethod.Append)]
[ARInvoiceNumberingCst]
protected void ARInvoice_RefNbr_CacheAttached(PXCache sender)
{ }
protected void ARInvoice_RowPersisting(PXCache cache, PXRowPersistingEventArgs e,
PXRowPersisting InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (ARInvoice)e.Row;
if (row != null)
{
//BB-<timestamp> as inv# for testing only
if (row.RefNbr == ARInvoiceNumberingCstAttribute.EnterRefNbr)
row.RefNbr = "BB-" + DateTime.Now.ToString("hhmmsstt");
}
}
}
}

Resources