Exception while updating Cache in POOrderEntry - acumatica

When a SOLine is added after creating the purchase order the values in SOLineSplit for "POType", "PONbr", "POLineNbr", "RefNoteID" is stored as NULL values.
When I add the same SOLine in a created Purchase order, I am trying to update the SOLineSplit NULL values with POLine values but I am getting an error in Pop up saying "Order Date Cannot be empty". Below is the code I have written in an event for updating values in Cache but getting an exception in graph.Actions.PressSave(); Can Anyone help me with this.
public PXSelectJoin<SOLineSplit, InnerJoin<POOrder, On<SOLineSplit.orderType, Equal<POOrder.sOOrderType>,
And<SOLineSplit.orderNbr, Equal<POOrder.sOOrderNbr>>>>,
Where<SOLineSplit.pOType, IsNull, And<SOLineSplit.pONbr, IsNull>>> UnmappedSoLine;
protected void POOrder_RowPersisted(PXCache sender, PXRowPersistedEventArgs e, PXRowPersisted BaseEvent)
{
POOrder row = e.Row as POOrder;
if (row != null && e.TranStatus == PXTranStatus.Completed)
{
SOLinePartial graph = PXGraph.CreateInstance<SOLinePartial>();
foreach (POLine poline in Base.Transactions.Select())
{
foreach (SOLineSplit sosplititem in UnmappedSoLine.Search<SOLineSplit.inventoryID>(poline.InventoryID))
{
sosplititem.POType = poline.OrderType;
sosplititem.PONbr = poline.OrderNbr;
sosplititem.POLineNbr = poline.LineNbr;
sosplititem.OrderDate = poline.OrderDate;
SOLineSplit sp = PXSelectJoin<SOLineSplit,
InnerJoin<POLine, On<
SOLineSplit.pOType, Equal<POLine.orderType>,
And<SOLineSplit.pONbr, Equal<POLine.orderNbr>, And<SOLineSplit.pOLineNbr, Equal<POLine.lineNbr>>>>>,
Where<POLine.orderNbr, Equal<Required<POOrder.orderNbr>>>>.Select(Base, row.OrderNbr);
if (sp != null)
sosplititem.RefNoteID = sp.RefNoteID;
graph.soLineSplit.Cache.Update(sosplititem);
}
}
graph.Actions.PressSave();
}
}

Related

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
}

CuryUnitCost on Purchase Order Screen, POLine DAC

I am trying to update the CuryUnitPrice field on Purchase Orders Screen with the custom field value from SOLine DAC when the purchase order is processes from Create Purchase Orders Screen. Any Idea on how to do this?
Right now I am trying to get the POLine based on the OrderNbr field. This field is Autonumbered and has " value in it. I tried all the possible ways to get the value of created POOrderNbr. But somehow I found it in RowSelectedEvent handler after triggering so many times. It is not a good idea to retrieve values for RowSeletedEventhandler. Is there any way I could get this value for updating the field either form Create Purchase Order screen or Purchase Order Screen. Below is the code I have written in RowSelectedEventHandler
protected virtual void POLine_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected del)
{
del.Invoke(cache, e);
POLine row = e.Row as POLine;
if (row != null)
{
if (row.OrderNbr != " <NEW>")
{
PXResultset<POLine> poLine = PXSelect<POLine, Where<POLine.orderNbr,
Equal<Required<POLine.orderNbr>>>>
.Select(Base, row.OrderNbr);
foreach (POLine p in poLine)
{
PXResultset<SOLineSplit> SoLineSplit = PXSelect<SOLineSplit, Where<SOLineSplit.pONbr,
Equal<Required<POOrder.orderNbr>>, And<SOLineSplit.pOLineNbr, Equal<Required<POLine.lineNbr>>>>>.Select(Base, row.OrderNbr, p.LineNbr);
foreach (SOLineSplit so in SoLineSplit)
{
SOLine soLine = PXSelectJoin<SOLine, InnerJoin<SOLineSplit, On<SOLine.orderNbr, Equal<Required<SOLineSplit.orderNbr>>>>,
Where<SOLineSplit.planID, Equal<Required<SOLineSplit.planID>>>>.Select(Base, so.OrderNbr, so.PlanID);
SOLineExt soLineExt = PXCache<SOLine>.GetExtension<SOLineExt>(soLine);
if (soLineExt.UsrUnitCost != null)
{
p.CuryUnitCost = soLineExt.UsrUnitCost;
Transactions.Update(p);
Base.Actions.PressSave();
}
}
}
}
}
}
Try RowPersisted event on POLine:
protected virtual void POLine_RowPersisted(PXCache cache, PXRowPeristedEventArgs e)
{
if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Insert ||
(e.Operation & PXDBOperation.Command) == PXDBOperation.Update)
{
}
}
Or POOrder if necessary:
protected virtual void POOrder_RowPersisted(PXCache cache, PXRowPeristedEventArgs e)
{
if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Insert ||
(e.Operation & PXDBOperation.Command) == PXDBOperation.Update)
{
}
}
I think the issue is that POOrderNbr is available only after the record has been persisted to database.

Acumatica - FieldDefaulting update ImageUrl from DAC extension

I am trying to update the Inventory Item ImageUrl if it is found to be null with some conditions. I have added a Usr field called UsrStyleImg to the Item Class screen. This field is for a basic image of an item and it is stored in the database. The functionality I want is if the Inventory Item does not have an image in the ImageUrl then it will default to the UsrStyleImg that is connected with the ItemClassID. ItemClassID is also found on the Stock Item Screen. Here is the code I have in the InventoryItemMaint graph:
protected void InventoryItem_ImageUrl_FieldDefaulting(PXCache cache, PXFieldDefaultingEventArgs e)
{
var row = (InventoryItem)e.Row;
if (row == null) return;
var item = (INItemClass)PXSelect<INItemClass, Where<INItemClass.itemClassID, Equal<Current<InventoryItem.itemClassID>>>>.Select(Base, row.ItemClassID);
var image = PXSelect<InventoryItem, Where<InventoryItem.imageUrl, Equal<Current<InventoryItem.imageUrl>>>>.Select(Base, row.ImageUrl);
if (image != null)
return;
else {
e.NewValue = item.GetExtension<INItemClassExt>().UsrStyleImg;
}
}
The code compiles fine but when I test with an Item that has an Item Class attached to it with an image in the INItemClass table called UsrStyleImg it does not populate to the imageUrl found in the Inventory Item table or the Stock Item screen. I have also tried this with FieldSelecting and using the e.ReturnValue with still the same results.
If I need more clarification please let me know.
Try using a RowSelecting Event
protected virtual void InventoryItem_RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
{
InventoryItem row = e.Row as InventoryItem;
//Extra checks to prevent infinite loops
if (row != null && !string.IsNullOrWhiteSpace(row.InventoryCD) && Base.Item.Cache.GetStatus(row) == PXEntryStatus.Notchanged)
{
if (!string.IsNullOrWhiteSpace(row.ItemClassID))
{
//You must always use a PXConnectionScope if Selecting during RowSelecting
using (new PXConnectionScope())
{
//If you're going to pass in a value in .Select, use Required instead of Current.
INItemClass itemClass = PXSelectReadonly<INItemClass, Where<INItemClass.itemClassID, Equal<Required<INItemClass.itemClassID>>>>.Select(Base, row.ItemClassID);
if (itemClass != null && string.IsNullOrWhiteSpace(row.ImageUrl))
{
INItemClassExt itemClassExt = itemClass.GetExtension<INItemClassExt>();
//To prevent unneeded update if it's blank
if (!string.IsNullOrWhiteSpace(itemClassExt.UsrStyleImg))
{
row.ImageUrl = itemClassExt .UsrStyleImg;
//Force set the status in the Cache, otherwise it infinite loops
Base.Item.Cache.SetStatus(row, PXEntryStatus.Updated);
Base.Item.Update(row);
}
}
}
}
}
}

Acumatica PXUIFieldAttribute SetError giving error

I'm trying to verify the value entered into my Support Split table, why am I unable to grab my ItemExtension?
protected void atcProjectCostCenterTable_CostCenterSplit_FieldVerifying(PXCache cache, PXFieldVerifyingEventArgs e, PXFieldVerifying InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (atcProjectCostCenterTable)e.Row;
if (row.ContractID > 0)
{
decimal? hun = 100;
PX.Objects.CT.ContractExt item = row.GetExtension<PX.Objects.CT.ContractExt>();
if (CostCenterSplit.Select().Count >= 1)
{
if (item.UsrCostCenterSum.Value != hun)
{
PXUIFieldAttribute.SetError<atcProjectCostCenterTable.costCenterSplit>(cache, row, "Support Percentages must equal 100%");
//throw new PXSetPropertyException("Cost Center Percentages must equal 100%", PXErrorLevel.Warning);
}
}
}
}
Nick, please replace
PX.Objects.CT.ContractExt item = row.GetExtension<PX.Objects.CT.ContractExt>();
with
PX.Objects.CT.ContractExt item = Base.Project.Current.GetExtension<PX.Objects.CT.ContractExt>();
or
PX.Objects.CT.ContractExt item = Base.Project.Cache.GetExtension<PX.Objects.CT.ContractExt>(Base.Project.Current);
Your current code can not work because ContractExt extends Contract DAC, not atcProjectCostCenterTable. Since PMProject is inherited from Contract, DAC extensions defined for the Contract DAC are also available for PMProject instances.
Contract con = Base.Project.Current;
PX.Objects.CT.ContractExt item = con.GetExtension<PX.Objects.CT.ContractExt>();

Resources