Tell me please how I can Hide the ADD LC button, since it is defined in the acumatica code that it is displayed by condition.
This is the standard acumatica code that displays this button
#region Actions
public PXAction<APInvoice> addLandedCost;
public PXAction<APInvoice> addLandedCost2;
[PXUIField(DisplayName = "Add LC", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select, Visible = true)]
[PXLookupButton]
[APMigrationModeDependentActionRestriction(
restrictInMigrationMode: true,
restrictForRegularDocumentInMigrationMode: true,
restrictForUnreleasedMigratedDocumentInNormalMode: true)]
public virtual IEnumerable AddLandedCost(PXAdapter adapter)
{
if (Base.Document.Current != null &&
Base.Document.Current.Released != true)
{
if (LandedCostDetailsAdd.AskExt((graph, view) =>
{
landedCostFilter.Cache.ClearQueryCacheObsolete();
landedCostFilter.View.Clear();
landedCostFilter.Cache.Clear();
LandedCostDetailsAdd.Cache.ClearQueryCacheObsolete();
LandedCostDetailsAdd.View.Clear();
LandedCostDetailsAdd.Cache.Clear();
}, true) == WebDialogResult.OK)
{
return AddLandedCost2(adapter);
}
}
return adapter.Get();
}
[PXUIField(DisplayName = "Add LC", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select, Visible = true)]
[PXLookupButton]
[APMigrationModeDependentActionRestriction(
restrictInMigrationMode: true,
restrictForRegularDocumentInMigrationMode: true,
restrictForUnreleasedMigratedDocumentInNormalMode: true)]
public virtual IEnumerable AddLandedCost2(PXAdapter adapter)
{
if (Base.Document.Current != null &&
Base.Document.Current.DocType.IsIn(APDocType.Invoice, APDocType.DebitAdj) &&
Base.Document.Current.Released == false &&
Base.Document.Current.Prebooked == false)
{
var landedCostDetails = LandedCostDetailsAdd.Cache.Updated.RowCast<POLandedCostDetailS>().Where(t => t.Selected == true).ToArray();
Base.AddLandedCosts(landedCostDetails);
landedCostDetails.ForEach(t => t.Selected = false);
}
return adapter.Get();
}
#endregion
#region Events
protected virtual void APInvoice_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
APInvoice document = e.Row as APInvoice;
if (document == null) return;
var invoiceState = Base.GetDocumentState(cache, document);
addLandedCost.SetVisible(invoiceState.IsDocumentInvoice || invoiceState.IsDocumentDebitAdjustment);
PXUIFieldAttribute.SetEnabled(LandedCostDetailsAdd.Cache, null, false);
bool allowAddLandedCost = invoiceState.IsDocumentEditable &&
invoiceState.LandedCostEnabled &&
!invoiceState.IsRetainageDebAdj;
addLandedCost.SetEnabled(allowAddLandedCost);
PXUIFieldAttribute.SetEnabled<POLandedCostDetailS.selected>(LandedCostDetailsAdd.Cache, null, allowAddLandedCost);
}
This line indicates under what condition to display. How can I override this condition in my extension class in APInvoice_RowSelected? e.g. display only if (invoiceState.IsDocumentInvoice)
addLandedCost.SetVisible(invoiceState.IsDocumentInvoice || invoiceState.IsDocumentDebitAdjustment);
Artem,
I would write an event override
protected virtual void APInvoice_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected baseHandler)
{
baseHandler.Invoke(cache, e);
// Now put your custom .SetEnabled function here and it will run after the base handler runs
}
Related
I have a need to modify the behavior of the Create Sales Order action on the Opportunity screen.
I'm trying to find the best / least intrusive method possible. I have a snippet of the base code below.
The changes I need to make are relatively simple. I need to populate some custom fields that exist on new SO.
What's the best approach?
public PXAction<CROpportunity> createSalesOrder;
[PXUIField(DisplayName = Messages.CreateSalesOrder, MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Select)]
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.DataEntry)]
public virtual IEnumerable CreateSalesOrder(PXAdapter adapter)
{
foreach (CROpportunity opportunity in adapter.Get<CROpportunity>())
{
Customer customer = (Customer)PXSelect<Customer, Where<Customer.bAccountID, Equal<Current<CROpportunity.bAccountID>>>>
.SelectSingleBound(this, new object[] { opportunity });
if (customer == null)
{
throw new PXException(Messages.ProspectNotCustomer);
}
if (CreateOrderParams.AskExtFullyValid((graph, viewName) => { }, DialogAnswerType.Positive))
{
Actions.PressSave();
PXLongOperation.StartOperation(this, delegate()
{
var grapph = PXGraph.CreateInstance<OpportunityMaint>();
grapph.Opportunity.Current = opportunity;
grapph.CreateOrderParams.Current = CreateOrderParams.Current;
grapph.DoCreateSalesOrder(CreateOrderParams.Current);
});
}
yield return opportunity;
}
}
protected virtual void DoCreateSalesOrder(CreateSalesOrderFilter param)
{
bool recalcAny = param.RecalculatePrices == true ||
param.RecalculateDiscounts == true ||
param.OverrideManualDiscounts == true ||
param.OverrideManualDocGroupDiscounts == true ||
param.OverrideManualPrices == true;
var opportunity = this.Opportunity.Current;
Customer customer = (Customer)PXSelect<Customer, Where<Customer.bAccountID, Equal<Current<CROpportunity.bAccountID>>>>.Select(this);
A simple override should accomplish your goal. Below is an example, which injects custom logic into the event handler SOOrder.RowInserted, during the Create Sales Order action. Then allow the action to finish normally. This approach extends the action with your custom logic, while retaining base code of the Action.
public class OpportunityMaint_Extension : PXGraphExtension<OpportunityMaint>
{
public delegate IEnumerable CreateSalesOrderDelegate(PXAdapter adapter);
[PXOverride]
public IEnumerable CreateSalesOrder(PXAdapter adapter, CreateSalesOrderDelegate baseMethod)
{
PXGraph.InstanceCreated.AddHandler<SOOrderEntry>((graph) =>
{
graph.RowInserted.AddHandler<SOOrder>((sender, e) =>
{
SOOrder order = (SOOrder)e.Row;
SOOrderExt orderExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(order);
orderExt.UsrCustomOne = "Howdy"; //assign anything you want here
});
});
return baseMethod(adapter);
}
}
}
I have a bound custom field grossprofit added into the SOLine grid which is calculated based on Item UnitCost, AverageCost and Qty.
I have the code written into SOLine_CuryUnitPrice_FieldSelecting event. However, it is not saving into the Database.
Also, I need a total of all lineitems for the custom field as a TotalGrossProfit on Order summary. It is calculating but for some reason it is not reflecting on screen.
Can anyone suggest?
Here is the code
public class SOOrderExtension : PXCacheExtension<SOOrder>
{
#region UsrTotalGrossProfit
public abstract class usrTotalGrossProfit : PX.Data.IBqlField
{
}
protected Decimal? _UsrTotalGrossProfit;
[PXCurrency(typeof(SOOrder.curyInfoID), typeof(SOOrder.orderWeight))]
[PXDefault(TypeCode.Decimal, "0.0")]
[PXUIField(DisplayName = "Total Gross Profit")]
public virtual Decimal? UsrTotalGrossProfit
{
get
{
return this._UsrTotalGrossProfit;
}
set
{
this._UsrTotalGrossProfit = value;
}
}
#endregion
}
public class SOLineExtension : PXCacheExtension<SOLine>
{
#region UsrGrossProfit
public abstract class usrGrossProfit : PX.Data.IBqlField
{
}
protected Decimal? _UsrGrossProfit;
[PXDBCurrency(typeof(SOOrder.curyInfoID), typeof(SOOrder.discTot))]
[PXDefault(TypeCode.Decimal, "0.0")]
[PXUIField(DisplayName = "Gross Profit")]
public virtual Decimal? UsrGrossProfit
{
get
{
return this._UsrGrossProfit;
}
set
{
this._UsrGrossProfit = value;
}
}
#endregion
}
public class SOOrderEntryExtension : PXGraphExtension<SOOrderEntry>
{
protected virtual void SOOrder_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
SOOrder row = e.Row as SOOrder;
if (row == null) return;
SOOrderExtension orderExtension = PXCache<SOOrder>.GetExtension<SOOrderExtension>(row);
foreach (SOLine soLine in Base.Transactions.Select())
{
SOLineExtension lineItem = PXCache<SOLine>.GetExtension<SOLineExtension>(soLine);
orderExtension.UsrTotalGrossProfit = orderExtension.UsrTotalGrossProfit + lineItem.UsrGrossProfit;
}
}
protected void SOLine_CuryUnitPrice_FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
var row = (SOLine)e.Row;
if (row != null && Base.Document.Current != null && Base.Document.Current.Status != "C")
{
SOLineExtension opportunity = PXCache<SOLine>.GetExtension<SOLineExtension>(row);
InventoryItem inv = PXSelect<InventoryItem,
Where<InventoryItem.inventoryID, Equal<Required<InventoryItem.inventoryID>>>>.Select(new PXGraph(), row.InventoryID);
if (inv.ItemType == "F") // Stock Item
{
opportunity.UsrGrossProfit = (row.UnitPrice - invc.AvgCost) * row.Qty;
}
Base.Save.Press();
Base.Persist();
}
}
}
These are the two events that I would use.. you may have to change them to your version of C# i am using 7...
protected void SOLine_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
if (!e.row is SOLine row) return;
if (!e.oldRow is SOLine oldRow) return;
//check for unwanted status, if true return
if (Base.Document.Current.Status == "C") return;
//check for the fields to have value, if no return
if (!row.UnitPrice.HasValue || !row.Qty.HasValue) return;
//get row extension
var rowExt = row..GetExtension<SOLineExtension>();
//select inventory record
var invItem = (InventoryItem)PXSelect<InventoryItem,
Where<InventoryItem.inventoryID, Equal<Required<InventoryItem.inventoryID>>>>.Select(Base, row.InventoryID);
if (invItem == null) return;
//add value to field, no persist or save
if (inv.ItemType == "F") // Stock Item
{
rowExt.UsrGrossProfit = (row.UnitPrice - invc.AvgCost) * row.Qty;
}
}
protected virtual void SOOrder_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
//this may not work in older versions of C#
if (!(e.row is SOOrder row) return;
var rowExt = row.GetExtension<SOOrderExtension>();
foreach (SOLine soLine in Base.Transactions.Select())
{
var lineItem = soLine.GetExtension<SOLineExtension>();
rowExt.UsrTotalGrossProfit += lineItem.UsrGrossProfit;
}
//this is probably why not showing up...
Base.Document.Cache.Update(Base.Document.Current);
}
I have a non persisted field in my Sales Order grid. I want the use to enter a value which will be used to update the unit price. i have tried catching all of the events for the grid/field but non of them trigger when the user enters a value. I do not need the update to happen on every character, just when the user hits tab to exist the field or enter.
Graph Extension:
using PX.Data;
using PX.Objects.CS;
using PX.Objects.IN;
namespace PX.Objects.SO
{
public class SOOrderEntry_Extension:PXGraphExtension<SOOrderEntry>
{
protected void SOLine_UsrMargin_FieldSelecting(PXCache cache, PXFieldSelectingEventArgs e)
{
var salesOrderEntry = (SOLine)e.Row;
if (salesOrderEntry == null)
return;
UpdateCustomFields(cache, salesOrderEntry);
}
protected void SOLine_UsrMargin_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
var salesOrderEntry = (SOLine)e.Row;
if (salesOrderEntry == null)
return;
UpdateCustomFields(cache, salesOrderEntry);
}
protected void SOLine_UsrMargin_FieldUpdating(PXCache cache, PXFieldUpdatingEventArgs e)
{
var salesOrderEntry = (SOLine)e.Row;
if (salesOrderEntry == null)
return;
UpdateCustomFields(cache, salesOrderEntry);
}
public class string_PROMODET : Constant<string>
{
public string_PROMODET() : base("PROMODET") { }
}
public class string_SALESPROMO : Constant<string>
{
public string_SALESPROMO() : base("SALESPROMO") { }
}
protected void SOLine_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e)
{
var salesOrderEntry = (SOLine)e.Row;
if (salesOrderEntry == null)
return;
UpdateCustomFields(cache, salesOrderEntry);
}
protected void SOLine_RowUpdating(PXCache cache, PXRowUpdatingEventArgs e)
{
var salesOrderEntry = (SOLine)e.Row;
if (salesOrderEntry == null)
return;
UpdateCustomFields(cache, salesOrderEntry);
}
protected void SOLine_RowInserted(PXCache cache, PXRowInsertedEventArgs e)
{
var salesOrderEntry = (SOLine)e.Row;
if (salesOrderEntry == null)
return;
UpdateCustomFields(cache, salesOrderEntry);
}
protected void SOLine_RowInserting(PXCache cache, PXRowInsertingEventArgs e)
{
var salesOrderEntry = (SOLine)e.Row;
if (salesOrderEntry == null)
return;
UpdateCustomFields(cache, salesOrderEntry);
}
protected void SOLine_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
var salesOrderEntry = (SOLine)e.Row;
if (salesOrderEntry == null)
return;
UpdateCustomFields(cache, salesOrderEntry);
}
protected void SOLine_RowSelecting(PXCache cache, PXRowSelectingEventArgs e)
{
var salesOrderEntry = (SOLine)e.Row;
if (salesOrderEntry == null)
return;
UpdateCustomFields(cache, salesOrderEntry);
}
private static void UpdateCustomFields(PXCache cache, SOLine salesOrderEntry)
{
SOLineExt soLineExt = PXCache<SOLine>.GetExtension<SOLineExt>(salesOrderEntry);
PXUIFieldAttribute.SetEnabled<SOLineExt.usrSalesPromotion>(cache, salesOrderEntry, false);
PXUIFieldAttribute.SetEnabled<SOLineExt.usrSalesPromotionDetails>(cache, salesOrderEntry, false);
using (new PXConnectionScope())
{
InventoryItem ii = PXSelect<InventoryItem, Where<InventoryItem.inventoryID, Equal<Required<SOLine.inventoryID>>>>
.Select(new PXGraph(), salesOrderEntry.InventoryID);
if (ii != null)
{
CSAnswers promotionDetail = PXSelect<CSAnswers,
Where2<
Where<CSAnswers.refNoteID, Equal<Required<InventoryItem.noteID>>>,
And<Where<CSAnswers.attributeID, Equal<string_PROMODET>,
Or<CSAnswers.attributeID, Equal<string_PROMODET>>>>>>
.Select(new PXGraph(), ii.NoteID);
CSAnswers promotion = PXSelect<CSAnswers,
Where2<
Where<CSAnswers.refNoteID, Equal<Required<InventoryItem.noteID>>>,
And<Where<CSAnswers.attributeID, Equal<string_SALESPROMO>,
Or<CSAnswers.attributeID, Equal<string_SALESPROMO>>>>>>
.Select(new PXGraph(), ii.NoteID);
INItemStats itemStats = PXSelect<INItemStats, Where<INItemStats.inventoryID, Equal<Required<SOLine.inventoryID>>>>
.Select(new PXGraph(), salesOrderEntry.InventoryID);
if (itemStats != null)
{
cache.SetValue<SOLineExt.usrLastCost>(salesOrderEntry, itemStats.LastCost);
cache.SetValue<SOLineExt.usrMargin>(salesOrderEntry, ((salesOrderEntry.CuryUnitPrice / itemStats.LastCost) - 1) * 100);
}
if (promotion != null)
{
cache.SetValue<SOLineExt.usrSalesPromotion>(salesOrderEntry, promotion.Value == "1" ? true : false);
cache.SetValue<SOLineExt.usrSalesPromotionDetails>(salesOrderEntry, promotion.Value == "1" ? promotionDetail.Value : "");
}
}
}
}
}
}
Cache Extension:
using PX.Data;
using System;
namespace PX.Objects.SO
{
public class SOLineExt : PXCacheExtension<PX.Objects.SO.SOLine>
{
#region UsrLastCost
[PXDecimal]
[PXUIField(DisplayName="Last Cost", Enabled = false, Visible = true, IsReadOnly = true)]
public virtual Decimal? UsrLastCost { get; set; }
public abstract class usrLastCost : IBqlField { }
#endregion
#region UsrSalesPromotion
[PXBool]
[PXUIField(DisplayName="Sales Promotion", Enabled = false, IsReadOnly = true)]
public virtual bool? UsrSalesPromotion { get; set; }
public abstract class usrSalesPromotion : IBqlField { }
#endregion
#region UsrSalesPromotionDetails
[PXString(64)]
[PXUIField(DisplayName="Sales Promotion Details", Enabled = false, IsReadOnly = true)]
public virtual string UsrSalesPromotionDetails { get; set; }
public abstract class usrSalesPromotionDetails : IBqlField { }
#endregion
#region UsrMargin
[PXDecimal]
[PXUIField(DisplayName="Margin")]
public virtual Decimal? UsrMargin { get; set; }
public abstract class usrMargin : IBqlField { }
#endregion
}
}
I am trying to add Add Items functionality in Opportunity like in Sales Order. I have gone through the code in SOOrderEntry and I have tried to replicate the same functionality.
The Add Item menu brings the smart panel with filter options, but it does not populate the data. I have compared the setting with Sales Order Add Item Smart panel settings and I did not miss anything.
I have replicated the table structure and events as in sales order with changes required for Opportunity.
#region SiteStatus Lookup
public PXFilter<OpportunitySiteStatusFilter> oppsitestatusfilter;
[PXFilterable]
[PXCopyPasteHiddenView]
public OpportunityStatusLookup<OpportunitySiteStatusSelected, OpportunitySiteStatusFilter> opportunitysitestatus;
public PXAction<CROpportunity> addOppBySite;
[PXUIField(DisplayName = "Add Stock Item", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
[PXLookupButton]
public virtual IEnumerable AddOppBySite(PXAdapter adapter)
{
oppsitestatusfilter.Cache.Clear();
if (opportunitysitestatus.AskExt() == WebDialogResult.OK)
{
return AddOppSelBySite(adapter);
}
oppsitestatusfilter.Cache.Clear();
opportunitysitestatus.Cache.Clear();
return adapter.Get();
}
public PXAction<CROpportunity> addOppSelBySite;
[PXUIField(DisplayName = "Add", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select, Visible = false)]
[PXLookupButton]
public virtual IEnumerable AddOppSelBySite(PXAdapter adapter)
{
foreach (OpportunitySiteStatusSelected line in opportunitysitestatus.Cache.Cached)
{
if (line.Selected == true && line.QtySelected > 0)
{
CROpportunityProducts newline = PXCache<CROpportunityProducts>.CreateCopy(Base.Products.Insert(new CROpportunityProducts()));
newline.SiteID = line.SiteID;
newline.InventoryID = line.InventoryID;
newline.SubItemID = line.SubItemID;
newline.UOM = line.SalesUnit;
//newline.AlternateID = line.AlternateID;
//newline = PXCache<SOLine>.CreateCopy(Transactions.Update(newline));
//if (newline.RequireLocation != true || PXAccess.FeatureInstalled<FeaturesSet.warehouseLocation>())
// newline.LocationID = null;
newline = PXCache<CROpportunityProducts>.CreateCopy(Base.Products.Update(newline));
//newline.Qty = line.QtySelected;
cnt = 0;
Base.Products.Update(newline);
}
}
opportunitysitestatus.Cache.Clear();
return adapter.Get();
}
protected virtual void OpportunitySiteStatusFilter_RowInserted(PXCache cache, PXRowInsertedEventArgs e)
{
OpportunitySiteStatusFilter row = (OpportunitySiteStatusFilter)e.Row;
if (row != null && Base.Products.Current != null)
row.SiteID = Base.Products.Current.SiteID;
}
int cnt;
public IEnumerable<PXDataRecord> ProviderSelect(BqlCommand command, int topCount, params PXDataValue[] pars)
{
cnt++;
return Base.ProviderSelect(command, topCount, pars);
}
#endregion
Regards,
R. Muralidharan
You may want to compare your custom SmartPanel aspx with out-of-box Add Stock Item SmartPanel of SalesOrderEntry screen. As #Hybridzz mentioned, most likely you haven’t set AutoSize property. You need to set AutoSize to True when Grid pagination is Enabled.
Hi I am trying to extend the ARPaymentEntry graph so I can do some extra stuff when the user releases the payment.
I have extended the paymententry graph and copied the Release action over like so
public PXAction<ARPayment> release;
[PXUIField(DisplayName = "Release", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)]
[PXProcessButton]
public virtual IEnumerable Release(PXAdapter adapter)
{
PXCache cache = Base.Document.Cache;
List<ARRegister> list = new List<ARRegister>();
foreach (ARPayment ardoc in adapter.Get<ARPayment>())
{
if (!(bool)ardoc.Hold)
{
cache.Update(ardoc);
list.Add(ardoc);
}
}
if (list.Count == 0)
{
throw new PXException(Messages.Document_Status_Invalid);
}
Base.Save.Press();
PXLongOperation.StartOperation(this, delegate()
{
if (SyncPaymentToRex(list))
{
ARDocumentRelease.ReleaseDoc(list, false);
}
});
return list;
}
If you look at the PXLongOperation I have a my own method I want to pass before it goes and releases the document.
Now this works for me but there is no user feedback on the screen (e.g. the controls arent disabled, no processing icon appears while its performing the operation etc) and also the screen doesnt reload, I have to manually reload the page before I can see the payment has been release etc
Can I get some help so I can get the page updating and reacting like it usually does on release but with my code in there as well?
Try this
public PXAction<ARPayment> release;
[PXUIField(DisplayName = "Release", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)]
[PXProcessButton]
[PXOverride]
public virtual IEnumerable Release(PXAdapter adapter)
{
PXCache cache = Base.Document.Cache;
List<ARRegister> list = new List<ARRegister>();
foreach (ARPayment ardoc in adapter.Get<ARPayment>())
{
if (!(bool)ardoc.Hold)
{
cache.Update(ardoc);
list.Add(ardoc);
}
}
if (list.Count == 0)
{
throw new PXException(Messages.Document_Status_Invalid);
}
Base.Save.Press();
PXLongOperation.StartOperation(this.Base, delegate()
{
if (SyncPaymentToRex(list))
{
ARDocumentRelease.ReleaseDoc(list, false);
}
});
return list;
}