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.
Related
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.
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;
}
}
}
}
}
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
}
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();
}
}
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();
}
}