I have override Invoices screen Release action to restrict the Invoice release if my condition satisfied and the custom error is thrown, but even the error is thrown and after closing that error, the Invoice still gets released but I want to restrict the Invoice release if error is thrown.
[PXOverride]
public virtual IEnumerable Release(PXAdapter adapter)
{
if (Base.Document.Current != null)
{
var paymentSetup = PaySetup.SelectSingle(Base.Document.Current.PaymentMethodID);
var paymentSettings = PaySettingDetails.SelectSingle(Base.Document.Current.PaymentMethodID);
if (paymentSettings != null && paymentSetup != null && paymentSetup.IsActive == true)
{
if (this.PayCurrentInvoice.Current != null && this.PayCurrentInvoice.Current.PayStatus.ToUpper() != KNPIConstants.PAID)
{
throw new PXException(KNPIMessages.NotPaid);
}
}
else
{
return Base.release.Press(adapter);
}
}
return null;
}
The Invoice should not gets released if the error is thrown but even though the error is throwing, after closing that error still the Invoice gets released.
Your override is wrong!
public delegate IEnumerable ReleaseDelegate(PXAdapter adapter);
[PXOverride]
public IEnumerable Release(PXAdapter adapter, ReleaseDelegate baseMethod)
{
var paymentSetup = PaySetup.SelectSingle(Base.Document.Current.PaymentMethodID);
var paymentSettings = PaySettingDetails.SelectSingle(Base.Document.Current.PaymentMethodID);
if (paymentSettings != null && paymentSetup != null && paymentSetup.IsActive == true)
{
if (this.PayCurrentInvoice.Current != null && this.PayCurrentInvoice.Current.PayStatus.ToUpper() != KNPIConstants.PAID)
{
throw new PXException(KNPIMessages.NotPaid);
}
}
return baseMethod(adapter);
}
Related
Anyone know if it's possible to combine a field like Item Class (we use a 3 letter code for each one) with a sequential number to get a unique Inventory ID?
e.g. we need all hardware to be HWR-00988 but all Shipping Supplies to be SUP-00989 and so on.
My workaround right now is to create an Attribute called ITEMCLASS and basically mirror the item class codes (HWR, SUP, etc) then add attributes to each Item Class and use the Inventory ID Segment Settings to make it look like it's pulling the actual Item Class.
Seems like this should exist though? I know the data exists in INITEMMENU already.
If you mean having a unique InventoryCD key that switches between constants HWR-XXXXX and SUP-XXXXX based on custom business rules (like appending Item Class); I don't think that's possible out-of-box without programming.
Usually this is accomplished with a custom attribute. Here's an example for [CuryID] attribute which computes key value based on arbitrary logic.
Usage, decorate key fields with custom attribute [CuryID]:
[PXDBString(5, IsUnicode = true)]
[PXDefault(typeof(AccessInfo.baseCuryID))]
[PXUIField(DisplayName = "Currency", ErrorHandling = PXErrorHandling.Never)]
[PXSelector(typeof(Currency.curyID))]
[CuryID]
public virtual String CuryID { get; set; }
For your scenario you would replace InventoryRaw with your custom attribute:
[PXDefault]
[InventoryRaw(IsKey = true, DisplayName = "Inventory ID")]
public virtual String InventoryCD { get; set; }
Custom attribute [CuryID], if key is autogenerated consider adding FieldDefaulting event:
public sealed class CuryIDAttribute : PXEventSubscriberAttribute, IPXFieldUpdatedSubscriber, IPXRowSelectedSubscriber, IPXRowUpdatingSubscriber, IPXRowUpdatedSubscriber, IPXFieldVerifyingSubscriber
{
public void FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
CurrencyInfo info = e.Row as CurrencyInfo;
if (info != null)
{
//reset effective date to document date first
info.SetDefaultEffDate(sender);
try
{
info.defaultCuryRate(sender);
}
catch (PXSetPropertyException ex)
{
sender.RaiseExceptionHandling(_FieldName, e.Row, sender.GetValue(e.Row, _FieldOrdinal), ex);
}
info.CuryPrecision = null;
}
}
public void RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
CurrencyInfo info = e.Row as CurrencyInfo;
if (info != null)
{
bool disabled = info.IsReadOnly == true || (info.CuryID == info.BaseCuryID);
PXUIFieldAttribute.SetEnabled<CurrencyInfo.curyMultDiv>(sender, info, !disabled);
PXUIFieldAttribute.SetEnabled<CurrencyInfo.sampleCuryRate>(sender, info, !disabled);
PXUIFieldAttribute.SetEnabled<CurrencyInfo.sampleRecipRate>(sender, info, !disabled);
PXUIFieldAttribute.SetEnabled<CurrencyInfo.curyRateTypeID>(sender, info, !disabled);
PXUIFieldAttribute.SetEnabled<CurrencyInfo.curyEffDate>(sender, info, !disabled);
PXUIFieldAttribute.SetEnabled<CurrencyInfo.baseCuryID>(sender, info, false);
PXUIFieldAttribute.SetEnabled<CurrencyInfo.displayCuryID>(sender, info, false);
PXUIFieldAttribute.SetEnabled<CurrencyInfo.curyID>(sender, info, true);
}
}
private bool? currencyInfoDirty = null;
public void RowUpdating(PXCache sender, PXRowUpdatingEventArgs e)
{
CurrencyInfo info = e.Row as CurrencyInfo;
if (info != null && info._IsReadOnly == true)
{
e.Cancel = true;
}
else
{
currencyInfoDirty = sender.IsDirty;
}
}
public void RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
CurrencyInfo info = e.Row as CurrencyInfo;
if (info != null)
{
CurrencyInfo old = e.OldRow as CurrencyInfo;
if (old != null && (String.IsNullOrEmpty(info.CuryID) || String.IsNullOrEmpty(info.BaseCuryID)))
{
info.BaseCuryID = old.BaseCuryID;
info.CuryID = old.CuryID;
}
if (currencyInfoDirty == false
&& info.CuryID == old.CuryID
&& info.CuryRateTypeID == old.CuryRateTypeID
&& info.CuryEffDate == old.CuryEffDate
&& info.CuryMultDiv == old.CuryMultDiv
&& info.CuryRate == old.CuryRate)
{
sender.IsDirty = false;
currencyInfoDirty = null;
}
}
}
public void FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
{
CurrencyInfo info = e.Row as CurrencyInfo;
if (info != null)
{
CMSetup CMSetup = info.getCMSetup(sender);
}
}
}
I am getting the following error when trying to delete a customer:
An unhandled exception has occurred in the function 'MoveNext'
I think i need to add this if statement or something similar.
if(Base.BAccount.Cache.GetStatus(CCustomer) != PXEntryStatus.InsertedDeleted || Base.BAccount.Cache.GetStatus(CCustomer) != PXEntryStatus.Deleted)
I found a couple of links with this issue but non that is specific to the customer page.
public delegate void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
using (var scope = new PXTransactionScope())
{
Customer row = this.Base.BAccount.Current;
if (row.ParentBAccountID == null)
{
CustomerMaint businessAccount = PXGraph.CreateInstance<CustomerMaint>();
PXResultset<Customer> Children = PXSelect<Customer, Where<Customer.parentBAccountID, Equal<Required<Customer.bAccountID>>>>.Select(Base, row.BAccountID);
foreach (Customer item in Children)
{
businessAccount.Clear();
businessAccount.BAccount.Current = PXSelectorAttribute.Select<Customer.bAccountID>(this.Base.BAccount.Cache, item.BAccountID) as Customer;
item.TermsID = row.TermsID;
item.Status = row.Status;
Contact defContact = PXSelect<Contact, Where<Contact.bAccountID, Equal<Required<BAccount.bAccountID>>, And<Contact.contactID, Equal<Required<BAccount.defContactID>>>>>.Select(businessAccount, item.BAccountID, item.DefContactID);
defContact.EMail = this.Base.DefContact.Current.EMail;
businessAccount.DefContact.Update(defContact);
businessAccount.BAccount.Update(item);
businessAccount.Save.PressButton();
}
}
baseMethod();
scope.Complete();
}
}
I am hoping there is a check I can build in or something that can eliminate the MoveNext error when deleting.
Try to use the same pattern as the base CustomerMaint class which uses UpdateChildAccounts and GetChildAccounts methods to modify the child accounts.
protected virtual void Customer_PrintDunningLetters_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
Customer row = (Customer)e.Row;
CheckExcludedFromDunning(cache, row);
UpdateChildAccounts<Customer.printDunningLetters>(cache, row, GetChildAccounts(sharedCreditPolicy: true));
}
protected virtual void Customer_Status_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
Customer row = e.Row as Customer;
if (row == null) return;
if (row.ParentBAccountID == null)
{
Func<Customer, bool> func;
string newValue = GetSharedCreditChildStatus(row.Status, out func);
UpdateChildAccounts<Customer.status>(cache, row, GetChildAccounts(sharedCreditPolicy: true).Where(func), newValue);
}
}
protected virtual void Customer_ConsolidateToParent_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
Customer row = (Customer)e.Row;
if (row == null) return;
if (row.ParentBAccountID == null)
{
IEnumerable<Customer> childs;
string message = PXMessages.LocalizeFormatNoPrefix(Messages.RelatedFieldChangeOnParentWarning,
PXUIFieldAttribute.GetDisplayName<Customer.consolidateToParent>(sender));
if ((childs = GetChildAccounts()).Any() && e.ExternalCall)
{
if (CurrentCustomer.Ask(message, MessageButtons.YesNo) == WebDialogResult.Yes)
{
UpdateChildAccounts<Customer.consolidateToParent>(sender, row, childs);
}
}
row.SharedCreditPolicy &= row.ConsolidateToParent;
}
else if (row.SharedCreditPolicy == true && row.ConsolidateToParent != true && (bool?)e.OldValue == true)
{
sender.SetValueExt<Customer.sharedCreditPolicy>(row, false);
}
}
protected virtual void UpdateChildAccounts<Field>(PXCache cache, Customer parent, IEnumerable<Customer> enumr, object sourceValue = null)
where Field : IBqlField
{
if (PXAccess.FeatureInstalled<FeaturesSet.parentChildAccount>() &&
parent != null &&
parent.ParentBAccountID == null)
{
sourceValue = sourceValue ?? cache.GetValue<Field>(parent);
foreach (Customer child in enumr)
{
if (sourceValue != cache.GetValue<Field>(child))
{
cache.SetValue<Field>(child, sourceValue);
cache.Update(child);
}
}
}
}
protected virtual IEnumerable<Customer> GetChildAccounts(bool sharedCreditPolicy = false, bool consolidateToParent = false, bool consolidateStatements = false)
{
if (PXAccess.FeatureInstalled<FeaturesSet.parentChildAccount>())
{
PXSelectBase<Customer> select = new PXSelect<Customer, Where<Customer.parentBAccountID, Equal<Current<Customer.bAccountID>>>>(this);
if (sharedCreditPolicy)
{
select.WhereAnd<Where<Customer.sharedCreditPolicy, Equal<True>>>();
}
if (consolidateToParent)
{
select.WhereAnd<Where<Customer.consolidateToParent, Equal<True>>>();
}
if (consolidateStatements)
{
select.WhereAnd<Where<Customer.consolidateStatements, Equal<True>>>();
}
return select.Select().RowCast<Customer>();
}
return Enumerable.Empty<Customer>();
}
Otherwise create a new DAC inheriting from Customer to hold the children and create a data view for them in the graph. You can then modify the children in that data view and they will be persisted automatically when user invoke the Save action from UI.
[Serializable]
public partial class OtherCustomer : Customer
{
public new abstract class bAccountID : PX.Data.BQL.BqlInt.Field<bAccountID> { }
}
PXSelect<OtherCustomer, Where< … >> OtherCustomers;
How would you write code to set a default value on a grid with the value of the field from the line above it?
One way would be this. Let us suppose you want to default the quantity on the Bills and Adjustments screen, with the quantity from the prior line. Consider using the pattern below.
public class APInvoiceEntry_Extension : PXGraphExtension<APInvoiceEntry>
{
#region Event Handlers
protected void APTran_Qty_FieldDefaulting(PXCache cache, PXFieldDefaultingEventArgs e)
{
var row = (APTran)e.Row;
if (row == null) return;
// get prior qty and default it here, unless it is already set.
if (row.Qty == null)
{
decimal? priorQty = GetPriorQtyInGrid(row);
if (priorQty != null)
{
e.NewValue = priorQty;
}
}
}
#endregion
#region Functions
public decimal? GetPriorQtyInGrid(APTran currentRow)
{
APTran lastline = null;
foreach (APTran line in Base.Transactions.Select())
{
if (currentRow == line)
{
break;
}
lastline = line;
}
if (lastline != null)
return lastline.Qty;
else
return null;
}
#endregion
}
I have an action button on the QuoteMaint graph. This action is in the actions folder. I set whether or not the button is enabled based on the quote status. When the user submits the quote, the action button should be enabled. I stepped through the code and it runs the routine to enable button, but on the screen it is not enabled. When I refresh the screen it is enabled with no issues. The code is below, thanks for your help!
public PXAction<CRQuote> printQuoteSummary;
[PXButton(CommitChanges = true, SpecialType = PXSpecialButtonType.Report)]
[PXUIField(DisplayName = "Print Quote - Summary")]
public IEnumerable PrintQuoteSummary(PXAdapter adapter)
{
Dictionary<string, string> parameters = new Dictionary<string, string>();
string actualReportID = "CR604510";
foreach (CRQuote item in adapter.Get<CRQuote>())
{
parameters[nameof(CRQuote.OpportunityID)] = item.OpportunityID;
parameters[nameof(CRQuote.QuoteNbr)] = item.QuoteNbr;
throw new PXReportRequiredException(parameters, actualReportID, "Report " + actualReportID);
}
return adapter.Get();
}
public override void Initialize()
{
base.Initialize();
Base.actionsFolder.AddMenuAction(printQuoteSummary);
Base.Actions.Move("PrintQuote", "printQuoteSummary");
printQuoteSummary.SetEnabled(Base.Quote.Current?.Status == CRQuoteStatusAttribute.Approved || Base.Quote.Current?.Status == CRQuoteStatusAttribute.Sent);
}
protected virtual void CRQuote_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
CRQuote quote = e.Row as CRQuote;
if (quote == null) return;
using (new PXConnectionScope())
{
CalcTotals(quote);
}
printQuoteSummary.SetEnabled(quote.Status == CRQuoteStatusAttribute.Approved || quote.Status == CRQuoteStatusAttribute.Sent);
}
Adding the additional argument for the event delegate resolved the issue in testing, please find sample below.
protected virtual void CRQuote_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected del)
{
del?.Invoke(cache, e);
CRQuote quote = e.Row as CRQuote;
if (quote == null) return;
using (new PXConnectionScope())
{
CalcTotals(quote);
}
PrintQuoteSummary.SetEnabled(quote.Status == CRQuoteStatusAttribute.Approved || quote.Status == CRQuoteStatusAttribute.Sent);
}
With this you may also remove reference to enable/disable in your initialize method as such it would be as follows.
public override void Initialize()
{
base.Initialize();
Base.actionsFolder.AddMenuAction(PrintQuoteSummary);
Base.Actions.Move("PrintQuote", "printQuoteSummary");
}
Is there a way to check if the delete action was pressed in Acumatica screen? This is because I have a code for reloading the StockItems screen, and whenever I am deleting an item it gives me the movenext error, but the item is being deleted successfully. Below is my Reloading Code:
[PXOverride]
public void Persist(Action persist)
{
persist();// this will call base Persist();
InventoryItemMaint grp = PXGraph.CreateInstance<InventoryItemMaint>();
InventoryItem inv = PXSelect<InventoryItem, Where<InventoryItem.inventoryCD, Equal<Required<InventoryItem.inventoryCD>>>>.Select(grp, this.Base.Item.Current.InventoryCD.Trim());
if (inv != null && inv.InventoryID.HasValue)
{
grp.Item.Current = grp.Item.Search<InventoryItem.inventoryID>(inv.InventoryID);
throw new PXRedirectRequiredException(grp, "Reloading Item");
}
}
As one of the ways I propose you to add to your DAC field IsDeleted, like this:
[PXBool]
[PXDefault(false)]
public bool? IsDeleted { get; set; }
then attach to RowDeleting(). For example like this:
protected virtual void ShipmentLine_RowDeleting(PXCache sender, PXRowDeletingEventArgs e)
{
ShipmentLine line = (ShipmentLine)e.Row;
line.IsDeleted = true;
}
And finally modify your code to following scenario:
[PXOverride]
public void Persist(Action persist)
{
ShipmentLine line = ViewShipmentLine.Current;
if(line.IsDeleted)
{
//do some logic which you consider as nesessary
}
persist();// this will call base Persist();
InventoryItemMaint grp = PXGraph.CreateInstance<InventoryItemMaint>();
InventoryItem inv = PXSelect<InventoryItem, Where<InventoryItem.inventoryCD,Equal<Required<InventoryItem.inventoryCD>>>>.Select(grp, this.Base.Item.Current.InventoryCD.Trim());
if (inv != null && inv.InventoryID.HasValue)
{
grp.Item.Current = grp.Item.Search<InventoryItem.inventoryID>(inv.InventoryID);
throw new PXRedirectRequiredException(grp, "Reloading Item");
}
}
Hope it helps.
You can follow the method suggested by Yura, OR you can acheive it in other way i think..
You can check the cache status of the record before calling base persist, if the cache status says it is deleted, dont do your redirect.
The code might be as below [NOT TESTED]
[PXOverride]
public void Persist(Action persist)
{
bool isDeletion = false;
InventoryItem CurrentItem = this.Base.Item.Cache.Current as InventoryItem ;
if(this.Base.Item.Cache.GetStatus(CurrentItem) == PXEntryStatus.Deleted) // Helps to check whether the operation is deletion
isDeletion = true;
persist();// this will call base Persist();
if(!isDeletion)
{
InventoryItemMaint grp = PXGraph.CreateInstance<InventoryItemMaint>();
InventoryItem inv = PXSelect<InventoryItem, Where<InventoryItem.inventoryCD, Equal<Required<InventoryItem.inventoryCD>>>>.Select(grp, this.Base.Item.Current.InventoryCD.Trim());
if (inv != null && inv.InventoryID.HasValue)
{
grp.Item.Current = grp.Item.Search<InventoryItem.inventoryID>(inv.InventoryID);
throw new PXRedirectRequiredException(grp, "Reloading Item");
}
}
}
EDIT :
I found that the above code will not work as the InventoryItem
this.Base.Item.Current // is returning null
and this is your base problem of movenext error, so i suggest you to update your code as follows
[PXOverride]
public void Persist(Action persist)
{
persist();// this will call base Persist();
InventoryItem CurrentItem = this.Base.Item.Current as InventoryItem;
if (CurrentItem != null)
{
InventoryItemMaint grp = PXGraph.CreateInstance<InventoryItemMaint>();
InventoryItem inv = PXSelect<InventoryItem, Where<InventoryItem.inventoryCD, Equal<Required<InventoryItem.inventoryCD>>>>.Select(grp, CurrentItem.InventoryCD.Trim());
if (inv != null && inv.InventoryID.HasValue)
{
grp.Item.Current = grp.Item.Search<InventoryItem.inventoryID>(inv.InventoryID);
throw new PXRedirectRequiredException(grp, "Reloading Item");
}
}
}