Get Associated BAccount from Contact? - acumatica

The grid I'm working with has a Selector for a Contact, on input I'm trying to check the associated Contacts Business Account for a my custom date field.
Error
protected void atcProjectDistributionTable_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e, PXRowUpdated InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (atcProjectDistributionTable)e.Row;
if (row.ContactID != null)
{
Contact con = PXSelectorAttribute.Select<atcProjectDistributionTable.contactID>(cache, row) as Contact;
row.ContactCD = con.FullName;
row.BAcc = con.BAccountID;
if (row.BAcc != null)
{
BAccount bAcc = PXSelectorAttribute.Select<atcProjectDistributionTable.bAcc>(cache, row) as BAccount;
PX.Objects.CR.BAccountExt ba = bAcc.GetExtension<PX.Objects.CR.BAccountExt>();
if (ba.UsrExpiring != null)
{
PXUIFieldAttribute.SetError<atcProjectDistributionTable.bAcc>(cache, row, "Termination date:" + ba.UsrExpiring + " Reason:" + ba.UsrExReason);
}
}
}
}

Not sure what event handler is in question right now: RowInserting, RowInserted or RowUpdated... Based on provided stack trace the error happens because of bAcc.GetExtension<PX.Objects.CR.BAccountExt>(); statement invoked when bAcc variable equals null. Unfortunately, error message is not very descriptive.
As mentioned in How to call SetPropertyException from another event handler? thread, none of 3 handlers mentioned here (RowInserting, RowInserted or RowUpdated) should ever be used to report errors or warnings to users. I would highly recommend refactoring your code to follow design of the framework.

Related

fiscal area in the supplier information tab of the purchase order

Could you help me, I am changing the tax area code by adaptation, however the taxes are not updated, what am I missing or how can I change the related taxes when I change the tax area?
This is my code, through this event that I'm doing.
protected void POLine_SiteID_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
var row = (POLine)e.Row;
var head = Base.Document.Current;
if (head == null) return;
if (row != null && row.OrderType == POOrderType.RegularOrder)
{
POLine line = PXSelect<POLine, Where<POLine.orderType,
Equal<Required<POLine.orderType>>,
And<POLine.orderNbr, Equal<Required<POLine.orderNbr>>>>>.Select(Base, row.OrderType, row.OrderNbr);
bool? xchange = false;
if (line != null)
{
INSite site = PXSelect<INSite, Where<INSite.siteID,
Equal<Required<INSite.siteID>>>>.Select(Base, line.SiteID);
if (site != null && line.SiteID == site.SiteID)
{
var ext = site.GetExtension<INSiteExt>();
if (ext != null)
{
head.TaxZoneID = ext.UsrTaxZone;
xchange = true;
}
}
if (xchange == true)
{
foreach (PEMclTaxZone zone in PXSelect<PEMclTaxZone,
Where<PEMclTaxZone.taxZoneID, Equal<Required<PEMclTaxZone.taxZoneID>>,
And<PEMclTaxZone.taxCategoryID, Equal<Required<PEMclTaxZone.taxCategoryID>>>>>.Select(Base, head.TaxZoneID, line.TaxCategoryID))
{
if (zone != null)
{
foreach (POTaxTran potax in PXSelect<POTaxTran,
Where<POTaxTran.orderType, Equal<Required<POTaxTran.orderType>>,
And<POTaxTran.orderNbr, Equal<Required<POTaxTran.orderNbr>>>>>.Select(Base, head.OrderType, head.OrderNbr))
{
if (potax != null)
{
potax.TaxID = zone.Taxid;
potax.TaxZoneID = zone.TaxZoneID;
Base.Taxes.Cache.Update(potax);
}
}
}
}
}
}
}
}
When I select the tax area manually, two elements are registered in the tax grid, if I do it by event it only updates the last one, I follow it by code and I see that if it updates, however, it does not reflect in the tax grid.
Here I show evidence, with images.
This step is with an event that is not working.
step 1
step 2:
step 3:
manually select the tax area, selected from the same tab.
step 1:
step 2:
That's how it should go, that's what I want the event to do.
Please tell me what I am failing in the event, I hope I have been clear, thanks.
The functions that grab these are the Tax Zone Extensions. You would want to override the GetDefaultTaxZone function of POOrderEntry
[PXOverride]
public virtual string GetDefaultTaxZone(POOrder row,
Func<POOrder, string> baseMethod)
{
//logic before base function
baseMethod(row);
//logic after base function
}
If you do not want any of the code to be run, feel free to copy the initial function and do not call the baseMethod delegate.

Unable to Copy UDF from POLine (PO301000) to POReceiptLine(PO302000) on "Enter PO Receipt" Action

I have created 2 UDF's in POLine and POReceiptLine, I am trying to copy values of those UDF's from POLine to POReceiptLine on "Enter PO Receipt" action in PO Screen(PO301000). My code is getting executed but the values are not getting copied. Please suggest, Thanks
protected void POReceipt_RowPersisting(PXCache cache, PXRowPersistingEventArgs e)
{
var row = (POReceipt)e.Row;
POReceiptLine row1 = new POReceiptLine();
if (Base.Document.Current != null)
{
foreach (POReceiptLine tran in Base.transactions.Select())
{
POLine xPOLine = PXSelect<POLine,
Where<POLine.orderNbr, Equal<Current<POLine.orderNbr>>,
And<POLine.orderType, Equal<Current<POLine.orderType>>>>>.Select(Base, tran.PONbr, tran.POType);
if (xPOLine != null)
{
POLineExt poLineExt = PXCache<POLine>.GetExtension<POLineExt>(xPOLine);
POReceiptLineExt poReceiptLineExt = PXCache<POReceiptLine>.GetExtension<POReceiptLineExt>(row1);
poReceiptLineExt.UsrWarrantyTerms = poLineExt.UsrWarrantyTerms;
poReceiptLineExt.UsrVendorWarrantyDate = poLineExt.UsrVendorWarrantyDate;
}
return;
}
}
}
####...Section 2..####### I have tried using this below code as well but No Luck.
protected virtual void _(Events.FieldDefaulting<POReceiptLineExt.usrWarrantyTerms> e)
{
POReceiptLine row = (POReceiptLine)e.Row;
if (row != null)
{
POReceiptLineExt receiptLineExt = row.GetExtension<POReceiptLineExt>();
POLine line = SelectFrom<POLine>
.Where<POLine.pONbr.IsEqual<#P.AsString>
.And<POLine.lineNbr.IsEqual<#P.AsInt>>>
.View.Select(Base, row.PONbr, row.POLineNbr);
POLineExt lineExt = line.GetExtension<POLineExt>();
if (lineExt?.UsrWarrantyTerms != null && receiptLineExt != null)
{
e.NewValue = receiptLineExt.UsrWarrantyTerms;
}
}
}
protected void POReceiptLine_ReceiptNbr_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
var row = (POReceiptLine)e.Row;
if (row == null) return;
cache.SetDefaultExt<POReceiptLineExt.usrWarrantyTerms>(row);
}
Not sure of your version, so I'll answer per version 2020R2 that I am running.
Observations:
You seem to be returning after the 1st POReceiptLine in the transactions view. More importantly, the placement would seem more appropriate on the POReceiptLine_RowPersisting event where you would not have to do the foreach within the RowPersisting. Lastly, off the top of my head, I can't recall if you need to update the cache when doing this in row persisting if you are working on the record being persisted, but you are working on a different record meaning you likely need to update the cache which gets tricky inside RowPersisting events. (For example, you may not know if the other cache was already persisted.)
Flow of the action:
POOrderEntry contains the action CreatePOReceipt which, in turn, initializes POReceiptEntry and creates the receipt from the order via CreateReceiptFrom(...) which then calls AddPOLine(...). Subsequently, the POReceiptLine is created via line = this.transactions.Insert(line);.
Recommendation:
In 2020R2, it seems your code belongs in POReceiptEntry, but you did not specify where you are putting it. If you placed it in POOrderEntry, the event never fires.
Try putting something like this in POReceiptEntry (do similar for the other field also).
protected virtual void _(Events.FieldDefaulting<POReceiptLineExt.UsrWarrantyTerms> e)
{
POReceiptLine row = (POReceiptLine) e.Row;
if(row != null)
{
POReceiptLineExt receiptLineExt = row.GetExtension<POReceiptLineExt>();
POLine line = SelectFrom<POLine>
.Where<POLine.pONbr.IsEqual<#P.AsString>
.And<POLine.lineNbr.IsEqual<#P.AsInt>>>
.View.Select(Base, row.PONbr, row.POLineNbr);
POLineExt lineExt = line.GetExtension<POLineExt>();
if(lineExt?.UsrWarrantyTerms != null && receiptLineExt != null)
{
e.NewValue = receiptLineExt.UsrWarrantyTerms;
}
}
}
Since the PONbr and LineNbr fields may not be set yet, you may need to use SetDefaultExt for those 2 fields at the appropriate place. (I'd suggest maybe trying in the POReceiptLine.POLineNbr FieldUpdated event.) I believe the insert from the action coming from POOrderEntry already has the fields filled in, but that may not be the case in every way the POReceiptLine record is created, so you want to be sure you are setting the values EVERY time that it is warranted.
I have got a solution to this issue i.e. by using field level event and calling the function from a field level event, the below code is working perfectly fine for me. Thanks.
protected void POReceiptLine_POLineNbr_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
var row = (POReceiptLine)e.Row;
CarryForwardFromPO(row);
}
public void CarryForwardFromPO(POReceiptLine row)
{
if (row.PONbr == null || row.POType == null || row.POLineNbr == null)
return;
POReceiptLineExt _polext = PXCache<POReceiptLine>.GetExtension<POReceiptLineExt>(row);
if (row.PONbr != null)
{
POLine xPOLine = PXSelect<POLine,
Where<POLine.orderNbr, Equal<Required<POLine.orderNbr>>,
And<POLine.orderType, Equal<Required<POLine.orderType>>,
And<POLine.lineNbr, Equal<Required<POLine.lineNbr>>>>>>.Select(Base, row.PONbr, row.POType, row.POLineNbr);
if (xPOLine != null)
{
POLineExt xPOLineExt = PXCache<POLine>.GetExtension<POLineExt>(xPOLine);
if (xPOLineExt != null)
{
if (xPOLineExt.UsrVendWarrantyType != null)
{
_polext.UsrVendWarrantyType = xPOLineExt.UsrVendWarrantyType.Trim();
}
if (xPOLineExt.UsrWarrantyTerms != null)
{
_polext.UsrWarrantyTerms = xPOLineExt.UsrWarrantyTerms.Trim();
}
if (xPOLineExt.UsrVendorWarrantyDate != null)
{
_polext.UsrVendorWarrantyDate = xPOLineExt.UsrVendorWarrantyDate;
}
}
}
}
}

How to update and save a record just after the record has been save for the first time?

I've got the OpportunityID field which is a numbering sequence, and when it's saved for the first time I want the UsrEBDR Field to be updated with the opportunityID value.
So on INSERTING into the database, I want to copy the OpportunityID field to the UsrEBDR Field.
The OpportunityID field is an Autonumbering Sequence so in the RowPersistingEvent it has not yet been calculated by the framework.
so I tried overriding the RowPersisted event but I get an exception on MoveNext and it doesnt save my record.
Do you have an idea?
protected virtual void CROpportunity_RowPersisted(PXCache sender, PXRowPersistedEventArgs e, PXRowPersisted del)
{
if (del != null) del(sender, e);
CROpportunity row = e.Row as CROpportunity;
if(row == null) return;
if(e.Operation == PXDBOperation.Insert && string.IsNullOrWhiteSpace(row.GetExtension<CROpportunityExt>().UsrEBDR) && e.TranStatus == PXTranStatus.Open)
{
Base.Opportunity.Current.GetExtension<CROpportunityExt>().UsrEBDR = row.OpportunityID;
Base.Persist();
}
}
Edit:
Thus far I got it working by doing this :
protected virtual void CROpportunity_RowPersisted(PXCache sender, PXRowPersistedEventArgs e, PXRowPersisted del)
{
if (del != null) del(sender, e);
CROpportunity row = e.Row as CROpportunity;
if(row == null) return;
if(e.Operation == PXDBOperation.Insert && string.IsNullOrWhiteSpace(row.GetExtension<CROpportunityExt>().UsrEBDR) && e.TranStatus == PXTranStatus.Completed)
{
row.GetExtension<CROpportunityExt>().UsrEBDR = row.OpportunityID;
using(PXTransactionScope ts = new PXTransactionScope())
{
var restrictOpportunityId = new PXDataFieldRestrict<CROpportunity.opportunityID>(row.OpportunityID);
var assignEBDR = new PXDataFieldAssign<CROpportunityExt.usrEBDR>(row.OpportunityID);
PXDatabase.Update<CROpportunity>(assignEBDR, restrictOpportunityId);
ts.Complete();
}
}
}
Yet I feel it's an improper use of the framework, and it's not really "clean", if someone has any idea how to make it clean.
Put your logic in RowPersisting and let the system save by itself. Calling Persist from Persist leads to many problems that are easily avoided if you simply modify the record before its persisted.
void CROpportunity_RowPersisting(PXCache sender, PXRowPersistingEventArgs e, PXRowPersisting del)
{
// Don't call save action or persist
// Just modify data and let the system save
}

Sales Price Updating Every Other Time

I have extended the SOOrderEntry graph and added the following code in order to update another line on the same sales order that is related to the current line that is being updated:
protected virtual void SOLine_RowUpdating(PXCache cache, PXRowUpdatingEventArgs e)
{
if (e.NewRow == null)
{
return;
}
SOLine soLine = (SOLine)e.NewRow;
SOLine relatedLine = Base.Transactions.Search<SOLine.inventoryID>
(456);
if (relatedLine != null)
{
relatedLine.Qty = soLine.Qty;
relatedLine.CuryUnitPrice = 24.20;
Base.Transactions.Update(relatedLine);
Base.Transactions.View.RequestRefresh();
}
}
When I try to test this by updating the Qty on the current line, the Unit Price on the related line only updates every other time that I update the Qty. The related item is a Non-stock item where the current item is a Stock Item.
I'm doing this in a Sales Demo environment on 18.102.0048
I tried this but, now the Extended Price is always 0.00:
protected virtual void SOLine_RowUpdating(PXCache cache, PXRowUpdatingEventArgs e)
{
if (e.NewRow == null)
{
return;
}
SOLine soLine = (SOLine)e.NewRow;
SOLine relatedLine = Base.Transactions.Search<SOLine.inventoryID>
(456);
if (relatedLine != null)
{
SOLine oldRelatedLine = PXCache<SOLine>.CreateCopy(relatedLine);
relatedLine.Qty = soLine.Qty;
relatedLine.CuryUnitPrice = 24.20;
Base.Transactions.Cache.RaiseRowUpdated(relatedLine, oldRelatedLine);
Base.Transactions.Update(relatedLine);
Base.Transactions.View.RequestRefresh();
}
}
When a field calculated value depends on another field value it's sometimes required to trigger events for the value to be recalculated.
In some cases it only requires firing the event for the field that you modified. Assigning values using the C# assignment operator (=) or the SetValue method will not trigger the events handler, SetValueExt method will:
// Doesn't trigger events
relatedLine.Qty = soLine.Qty;
cache.SetValue<SOLine.qty>(relatedLine, soLine.Qty);
// This will trigger events
cache.SetValueExt<SOLine.qty>(relatedLine, soLine.Qty);
There are some cases where you need to fire events for the whole row too. You can use the RaiseXxxYyy methods to do that. I used Current in the example but you might have to adapt it if Current is not the row being modified:
SOLine oldRowCopy = PXCache<SOLine>.CreateCopy(Base.Transactions.Current);
Base.Transactions.Current.Qty = soLine.Qty;
Base.Transactions.Cache.RaiseRowUpdated(Base.Transactions.Current, oldRowCopy);
EDIT:
Looking at updated question, it might not make much of a difference but I suggest you switch the order of these 2 lines:
Base.Transactions.Cache.RaiseRowUpdated(relatedLine, oldRelatedLine);
Base.Transactions.Update(relatedLine);
Like this:
// You want the value in Cache to be updated
Base.Transactions.Update(relatedLine);
// After Cache value is set you want to raise the events
Base.Transactions.Cache.RaiseRowUpdated(relatedLine, oldRelatedLine);
Thanks to HB_ACUMATICA, here's the code that resolved this:
protected virtual void SOLine_RowUpdating(PXCache cache, PXRowUpdatingEventArgs e)
{
if (e.NewRow == null)
{
return;
}
SOLine soLine = (SOLine)e.NewRow;
SOLine relatedLine = Base.Transactions.Search<SOLine.inventoryID>(456);
if (relatedLine != null)
{
SOLine oldRelatedLine = PXCache<SOLine>.CreateCopy(relatedLine);
relatedLine.Qty = soLine.Qty;
relatedLine.CuryUnitPrice = 24.20;
object outPrice = 0.01;
outPrice = (relatedLine.CuryUnitPrice * relatedLine.Qty);
cache.SetValueExt<SOLine.curyExtPrice>(relatedLine, outPrice);
Base.Transactions.Cache.RaiseRowUpdated(relatedLine, oldRelatedLine);
Base.Transactions.Update(relatedLine);
Base.Transactions.View.RequestRefresh();
}
}

How to set First Dropdown value as default for first line in grid then Second value defaults from second line in grid

In the Product drop-down, there are 2 values, First value (Product) will default for first line only and Second value (Co-Product) will default from the second line.
I tried this in FieldDefaulting event
protected void TSFormulaProdsNCoProds_Product_FieldDefaulting(PXCache cache, PXFieldDefaultingEventArgs e)
{
var row = (TSFormulaProdsNCoProds)e.Row;
if (row == null)
return;
if (TSFormProdsNCoProds.Select().Count == 0)
{
e.NewValue = "P";
}
else
{
e.NewValue = "C";
}
}
Can anyone provide a suggestion to me?
Most likely you have PXDefault attribute decorated at DAC level. In that case, you need to set Cancel flag to prevent execution of FieldDefaulting event handlers that are defined in attributes.
Example :-
protected void TSFormulaProdsNCoProds_Product_FieldDefaulting(PXCache cache, PXFieldDefaultingEventArgs e)
{
var row = (TSFormulaProdsNCoProds)e.Row;
if (row == null)
return;
e.NewValue = (TSFormProdsNCoProds.Select().Count == 0) ? "P" : "C";
e.Cancel = true;
}
This is explained in Example 5.2: Inserting a Default Detail Data Record of T200 Acumatica Framework Fundamental course.

Resources