I am extending ARInvoiceEntry graph and adding an even handler for RowPersisting, but the only data I see in the invoice is ShipAddressID:
namespace PX.Objects.AR
{
public class ARInvoiceEntry_Extension : PXGraphExtension<ARInvoiceEntry>
{
protected void ARInvoice_RowPersisting(PXCache cache, PXRowPersistingEventArgs e, PXRowPersisting InvokeBaseHandler)
{
var row = (ARInvoice)e.Row;
if (row != null)
{
// ???
}
}
}
}
Do I need to use that ShipAddressID and run a query? If the address is an override, query wouldn't work.
You need to select the current ARShippingAddress record from the base graph Shipping_Address dataview.
public void ARInvoice_RowPersisting(PXCache cache, PXRowPersistingEventArgs e)
{
ARInvoice invoice = e.Row as ARInvoice;
ARShippingAddress shipAddress = Base.Shipping_Address.Select();
if (invoice != null && shipAddress != null)
{
throw new PXException("Address Line 1: " + shipAddress.AddressLine1 + Environment.NewLine +
"Address Line 2: " + shipAddress.AddressLine2 + Environment.NewLine +
"Address Line 3: " + shipAddress.AddressLine3 + Environment.NewLine);
}
}
Related
How can I copy the note from a SalesOrder to a Shipment as the shipment is created?
I am trying to use the PXNoteAttribute.GetNote()/PXNoteAttribute.SetNote() functions, but GetNote keeps turning up blank.
#region Event Handlers
string notetext;
protected void SOShipLine_RowInserted(PXCache cache, PXRowInsertedEventArgs e, PXRowInserted InvokeBaseHandler)
{
if(InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (SOShipLine)e.Row;
SOOrder SalesOrder = (SOOrder)PXSelectorAttribute.Select<SOShipLine.origOrderNbr>(cache, e.Row);
string note = PXNoteAttribute.GetNote(cache, SalesOrder);
notetext = note;
}
protected void SOShipment_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e, PXRowUpdated InvokeBaseHandler)
{
if(InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (SOShipment)e.Row;
PXNoteAttribute.SetNote(cache, cache.Current, notetext);
}
The cache you are referencing in your code is the cache for the ShipLine. You need to reference the SalesOrder cache for GetNote() to function properly. You can use Base.Caches[typeof(SOOrder)].
Like so:
#region Event Handlers
string notetext;
protected void SOShipLine_RowInserted(PXCache cache, PXRowInsertedEventArgs e, PXRowInserted InvokeBaseHandler)
{
if(InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (SOShipLine)e.Row;
SOOrder SalesOrder = (SOOrder)PXSelectorAttribute.Select<SOShipLine.origOrderNbr>(cache, e.Row);
string note = PXNoteAttribute.GetNote(Base.Caches[typeof(SOOrder)], SalesOrder);
notetext = note;
}
protected void SOShipment_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e, PXRowUpdated InvokeBaseHandler)
{
if(InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (SOShipment)e.Row;
PXNoteAttribute.SetNote(cache, cache.Current, notetext);
}
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;
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");
}
I have modified portal copy order functionality to carry forward the source order shipping address detail into new order.
I have added a custom field to Portalcartlines DAC to store source order type & order number and while Proceed to Checkout action I am filling the address from the source order.
The address is properly carry forward to new order, but while saving I am getting error.
Error: Inserting 'Shipping Address' record raised at least one error. Please review the errors. Error: 'RevisionID' cannot be empty. Error: 'Country' cannot be empty
I am using the following code
public class PortalCardLinesExtn : PXCacheExtension<SP.Objects.IN.PortalCardLines>
{
#region UsrSourceOrderType
[PXDBString(2)]
//[PXUIField(DisplayName = "SourceOrderType")]
public virtual string UsrSourceOrderType { get; set; }
public abstract class usrSourceOrderType : IBqlField { }
#endregion
#region UsrSourceOrderNbr
[PXDBString(15)]
//[PXUIField(DisplayName = "SourceOrderNbr")]
public virtual string UsrSourceOrderNbr { get; set; }
public abstract class usrSourceOrderNbr : IBqlField { }
#endregion
}
public class InventoryCardMaint_Extension : PXGraphExtension<InventoryCardMaint>
{
public PXAction<PortalCardLine> ProceedToCheckOut;
[PXButton]
[PXUIField(DisplayName = "Proceed to Checkout")]
public IEnumerable proceedToCheckOut(PXAdapter adapter)
{
Base.DocumentDetails.Cache.Persist(PXDBOperation.Update);
Base.DocumentDetails.Cache.Persist(PXDBOperation.Insert);
Base.DocumentDetails.Cache.Persist(PXDBOperation.Delete);
foreach (PXCache value in Base.Caches.Values)
{
value.IsDirty = false;
}
SOOrderEntry sOOrderEntry = PXGraph.CreateInstance<SOOrderEntry>();
SOOrder sOOrder = sOOrderEntry.Document.Cache.CreateInstance() as SOOrder;
sOOrder = sOOrderEntry.Document.Insert();
sOOrderEntry.Document.Cache.SetValueExt<SOOrderExt.isSecondScreen>(sOOrder, 1);
//sOOrderEntry.Document.Cache.SetValueExt<SOOrderExt.overrideShipment>(sOOrder, true);
SOOrderExt extension = PXCache<SOOrder>.GetExtension<SOOrderExt>(sOOrder);
SOShippingContact sOShippingContact = sOOrderEntry.Shipping_Contact.Cache.Current as SOShippingContact;
SOShippingAddress sOShippingAddress = sOOrderEntry.Shipping_Address.Cache.Current as SOShippingAddress;
PortalCardLines prow = Base.DocumentDetails.Current;
if (prow != null)
{
PortalCardLinesExtn extn = Base.DocumentDetails.Cache.GetExtension<PortalCardLinesTSExtn>(prow);
if (!string.IsNullOrEmpty(extn.UsrSourceOrderNbr) && !string.IsNullOrEmpty(extn.UsrSourceOrderType))
{
SOOrder order = PXSelect<SOOrder, Where<SOOrder.orderType, Equal<Required<SOOrder.orderType>>, And<SOOrder.orderNbr, Equal<Required<SOOrder.orderNbr>>>>>.Select(Base, extn.UsrSourceOrderType, extn.UsrSourceOrderNbr);
if (order != null)
{
SOOrderExt sourceextension = PXCache<SOOrder>.GetExtension<SOOrderExt>(order);
sOOrderEntry.Document.Cache.SetValueExt<SOOrderExt.comment>(sOOrder, sourceextension.Comment);
SOShippingContact contact = PXSelect<SOShippingContact, Where<SOShippingContact.contactID, Equal<Required<SOShippingContact.contactID>>>>.Select(Base, order.ShipContactID);
SOShippingAddress address = PXSelect<SOShippingAddress, Where<SOShippingAddress.addressID, Equal<Required<SOShippingAddress.addressID>>>>.Select(Base, order.ShipAddressID);
contact.OverrideContact = true;
address.OverrideAddress = true;
sOShippingAddress.OverrideAddress = true;
sOShippingContact.OverrideContact = true;
if (contact != null)
{
sOShippingContact.FullName = contact.FullName;
sOShippingContact.Attention = contact.Attention;
sOShippingContact.Phone1 = contact.Phone1;
sOShippingContact.Email = contact.Email;
}
if (address != null)
{
sOShippingAddress.AddressLine1 = address.AddressLine1;
sOShippingAddress.AddressLine2 = address.AddressLine2;
sOShippingAddress.AddressLine3 = address.AddressLine3;
sOShippingAddress.City = address.City;
sOShippingAddress.CountryID = address.CountryID;
sOShippingAddress.State = address.State;
sOShippingAddress.PostalCode = address.PostalCode;
}
sOOrderEntry.Shipping_Contact.Cache.Update(sOShippingContact);
sOOrderEntry.Shipping_Address.Cache.Update(sOShippingAddress);
}
}
}
PXRedirectHelper.TryRedirect(sOOrderEntry, PXRedirectHelper.WindowMode.Same);
return adapter.Get();
}
}
I have tried it on customerid field updated event and this solved the issue.
The code is given below
protected void SOOrder_CustomerID_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e, PXFieldUpdated InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (SOOrder)e.Row;
if (row == null) return;
SOOrderExt ext = cache.GetExtension<SOOrderExt>(row);
PortalCardLines prow = Base1.DocumentCardDetails.SelectSingle();
if (prow != null)
{
PortalCardLinesTSExtn extn = PXCache<PortalCardLines>.GetExtension<PortalCardLinesTSExtn>(prow);
if (!string.IsNullOrEmpty(extn.UsrSourceOrderNbr) && !string.IsNullOrEmpty(extn.UsrSourceOrderType))
{
//UsrDeliveryNotes
SOOrder order = PXSelect<SOOrder, Where<SOOrder.orderType, Equal<Required<SOOrder.orderType>>, And<SOOrder.orderNbr, Equal<Required<SOOrder.orderNbr>>>>>.Select(Base, extn.UsrSourceOrderType, extn.UsrSourceOrderNbr);
if (order != null )
{
string notes = PXNoteAttribute.GetNote(Base.Document.Cache, order);
ext.Comment = notes;
row.ShipAddressID = order.ShipAddressID;
row.ShipContactID = order.ShipContactID;
}
}
}
}
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);
}