Customer created restriction group for each customer class and customer classes are assigned to default restriction group.
Eg. Customer Class ABC assigned with default Restriction group All Customer and also a Restriction Group created in the name of ABC.
While creating new customer using Customer class ABC it automatically adds All customer restriction group I am trying to add Restriction group ABC to the customer while saving the customer.
The following Code i have tried and it throws error.
public delegate Int32 PersistDelegate(Type cacheType, PXDBOperation operation);
[PXOverride]
public Int32 Persist(Type cacheType, PXDBOperation operation, PersistDelegate baseMethod)
{
Customer cust = Base.CurrentCustomer.Current;
if (cacheType.Name.ToLower() == "customer" && (operation == PXDBOperation.Insert && cust.BAccountID != null))
{
ARAccessDetail grp = PXGraph.CreateInstance<ARAccessDetail>();
grp.Customer.Current = grp.Customer.Search<Customer.acctCD>(Base.CurrentCustomer.Current.AcctCD);
bool bfound = false;
foreach (PXResult<RelationGroup> group in grp.Groups.Select())
{
RelationGroup row = group;
if (row.GroupName == cust.CustomerClassID)
{
row.Included = true;
grp.Groups.Update(row);
bfound = true;
break;
}
}
if (bfound)
grp.SaveCustomer.Press();
}
return baseMethod(cacheType, operation);
}
Update
The above code is not giving any error now, but the restriction group is not enabled for the customer.
Related
I have a custom action on screen CR304000 - OpportunityMaint in the Quotes tab of the grid view that marks a field called IsPrimary in the CRQuote DAC as true for the current record in the Quotes view. These project Quotes are associated with the current opportunity as well as with a related PMQoute in the PMQuoteMaint BLC. The PMQuoteMaint BLC has a view called Activties that has all the CRActivities associated with the PMQuote. I created a custom field in the CRActivity called IsPrimary and added it to the Activities view in the PMQuoteMaint BLC grid.
My goal was to override the Action in the OpportunityMaint to update the IsPrimary field in CRActivity to true or false depending on what the Action in OpportunityMaint is toggling. However, my IsPrimary field in PMQuoteMaint is not toggling. My code attempts to get Current Quote OpportunityMain and then create a PMQuoteMaint graph and set the Current record. Then iterate though Activies view and set the IsPrimary field accordingly. Like I said, not having success because I'm not sure that my code is successfully retrieving the correct Activities.
There might be a better way to access CRActivity associated with a PMQuote, but I'm not sure. Any help would be appreciated. Here is code:
public virtual IEnumerable PrimaryQuote(PXAdapter adapter)
{
foreach (CROpportunity opp in adapter.Get())
{
if (Quotes.Current?.IsPrimary != true)
{
var selectExistingPrimary = new PXSelect<CRQuote, Where<CRQuote.quoteID,
Equal<Required<CRQuote.quoteID>>>>(this);
CRQuote primary = selectExistingPrimary.Select(opp.DefQuoteID);
if (primary != null && primary.QuoteID != Quotes.Current.QuoteID && primary.Status ==
PM.PMQuoteStatusAttribute.Closed)
{
throw new PXException(PM.Messages.QuoteIsClosed, opp.OpportunityID,
primary.QuoteNbr);
}
var quoteID = Quotes.Current.QuoteID;
var opportunityID = this.Opportunity.Current.OpportunityID;
this.Persist();
PXDatabase.Update<Standalone.CROpportunity>(
new PXDataFieldAssign<Standalone.CROpportunity.defQuoteID>(quoteID),
new PXDataFieldRestrict<Standalone.CROpportunity.opportunityID>(PXDbType.VarChar,
255, opportunityID, PXComp.EQ)
);
this.Cancel.Press();
CROpportunity rec = this.Opportunity.Search<CROpportunity.opportunityID>
(opportunityID);
yield return rec;
}
yield return opp;
}
``` My OverRide
public PXAction<CROpportunity> primaryQuote;
[PXUIField(DisplayName = Messages.MarkAsPrimary)]
[PXButton]
public virtual IEnumerable PrimaryQuote(PXAdapter adapter)
{
// this is currently selected record in quotes grid
var currQuoteNbr = Base.Quotes.Current.QuoteNbr;
bool isPrimary2;
foreach (CRQuote quote in Base.Quotes.Select())
{
var quoteNbr = quote.QuoteNbr;
if(quoteNbr.Trim() == currQuoteNbr.Trim())
{
isPrimary2 = true;
}
else
{
isPrimary2 = false;
}
PXTrace.WriteInformation(string.Format("Quote: {0} Value:
{1}",quoteNbr.ToString(),isPrimary2.ToString()));
var PMQuoteMaintGraph = PXGraph.CreateInstance<PMQuoteMaint>();
PMQuoteMaintGraph.Quote.Current = PMQuoteMaintGraph.Quote.Search<PMQuote.quoteNbr>
(quoteNbr.Trim()); // this is the current quote
foreach (CRActivity activity in PMQuoteMaintGraph.Activities.Select())
{
CRActivityExt itemExt = PXCache<CRActivity>.GetExtension<CRActivityExt>(activity);
itemExt.UsrPrimary = isPrimary2;
PMQuoteMaintGraph.Activities.Cache.Persist(PXDBOperation.Update);
PXDatabase.Update<CRActivity>(new PXDataFieldAssign<CRActivityExt.usrPrimary>
(isPrimary2));
}
}
return Base.primaryQuote.Press(adapter);
}
when we are creating the Shipment “Is-Component” field we added newly in Sales Order Screen when we checked this field and when we process the shipment in Shipment screen the particular inventory data item is not passing only for checked ” Is component “item , unchecked items of “Is component” item are able to pass to shipment screen .
[PXOverride]
public IEnumerable Action(PXAdapter adapter, Nullable<Int32> actionID, Nullable<DateTime> shipDate, String siteCD, String operation, String ActionName, ActionDelegate baseMethod)
{
if (actionID == 1)
{
SOShipmentEntry ShipGraph = PXGraph.CreateInstance<SOShipmentEntry>();
PXGraph.InstanceCreated.AddHandler<SOShipmentEntry>((graph) =>
{
ShipGraph.RowInserting.AddHandler<SOShipLine>((sender, e) =>
{
foreach (SOLine line in Base.Transactions.Select())
{
ShipGraph.Transactions.Current = PXSelect<SOShipLine, Where<SOShipLine.shipmentNbr, Equal<Required<SOShipLine.shipmentNbr>>>>.Select(Base, line.InventoryID, line.OrderNbr);
SOShipLine ShipLine = new SOShipLine();
SOLineExt NonStklnExt = line.GetExtension<SOLineExt>();
if (ShipGraph.Transactions.Current == null)
{
//if (NonStklnExt.UsrIsComponent == true || NonStklnExt.UsrIsComponent == false || NonStklnExt.UsrInvFlag == true || NonStklnExt.UsrInvFlag == false || NonStklnExt.UsrStkInventoryID == null || NonStklnExt.UsrStkInventoryID != null)
//{
ShipLine.InventoryID = line.InventoryID;
ShipLine.TranDesc = line.TranDesc;
// }
ShipGraph.Transactions.Insert(ShipLine);
}
}
Base.Transactions.View.RequestRefresh();
});
});
}
return baseMethod(adapter, actionID, shipDate, siteCD, operation, ActionName);
}
I can assume that these items are added based on the flag, not always added based on the item. If that is the case, I would check the Require Shipment flag on the Non-Stock item to have it automatically added.
I would go at this by overriding the SOShipmentEntry CreateShipment function, rather than the SOShipLine Insert handler. Ensure you override the one that has the QuickProcessFlow.
From there, I would search for the initial order and order lines as you are doing after the shipment is created. Your logic is checking if the flags are checked or unchecked on the line, and should be updated. For example, if you wanted to find all items that are not on the shipment that have the component checked that are not auto added based on the NonStockShip flag, I would go at it like this:
public delegate void CreateShipmentDelegate(SOOrder order, Nullable<Int32> SiteID, Nullable<DateTime> ShipDate, Nullable<Boolean> useOptimalShipDate, String operation, DocumentList<SOShipment> list, ActionFlow quickProcessFlow);
[PXOverride]
public void CreateShipment(SOOrder order, Nullable<Int32> SiteID, Nullable<DateTime> ShipDate, Nullable<Boolean> useOptimalShipDate, String operation, DocumentList<SOShipment> list, ActionFlow quickProcessFlow, CreateShipmentDelegate baseMethod)
{
baseMethod(order, SiteID, ShipDate, useOptimalShipDate, operation, list, quickProcessFlow);
if (order == null)
return; //do not process for shipments that did not have an order sent for some reason
//get all lines for non-stockitems that are not flagged to be shipped that have the component checked
var SalesOrderLines = PXSelectJoin<
SOLine,
InnerJoin<InventoryItem,
On<SOLine.inventoryID, Equal<InventoryItem.inventoryID>>>,
Where<SOLine.orderNbr, Equal<Required<SOLine.orderNbr>>,
And<SOLine.orderType, Equal<Required<SOLine.orderType>>,
And<InventoryItem.nonStockShip, Equal<False>,
And<InventoryItem.stkItem, Equal<False>,
And<SOLineExt.usrIsComponent, Equal<True>>>>>>>
.Select(Base, order.OrderNbr, order.OrderType);
foreach(SOLine SalesOrderLine in SalesOrderLines)
{
//double check that they were not added.... and match the orig line nbr
var ShipmentLinesForItem = PXSelect<
SOShipLine,
Where<SOShipLine.shipmentNbr, Equal<Current<SOShipment.shipmentNbr>>,
And<SOShipLine.shipmentType, Equal<Current<SOShipment.shipmentType>>,
And<SOShipLine.inventoryID, Equal<Required<SOShipLine.inventoryID>>,
And<SOShipLine.origLineNbr, Equal<Required<SOShipLine.origLineNbr>>>>>>>
.Select(Base, SalesOrderLine.InventoryID, SalesOrderLine.LineNbr);
if (ShipmentLinesForItem.Count == 0)
{
//create your shipment lines for these items
SOShipLine ShipmentLine = new SOShipLine();
//set fields required. See function SOOrderEntry.CreateShipmentFromSchedules for examples of fields that should be set.
Base.Transactions.Insert(ShipmentLine);
}
}
}
How can a particular sales order be cancelled via code? Perhaps I can call the ProcessOrders graph, loop through the select orders, and execute the Cancel Order method. Unfortunately I see no such method. The drop-down action is driven by the automation menu. I do not find a cancel order action in the standard sales order entry graph. So what is the best way to accomplish the goal?
Via code, I can manually set the cancelled flag and status. This seems to work, but I'm not sure that is recommended. Seems like I'm skipping something, and the automation menu should be the way.
I'm copying the answer from this Acumatica blog post since it does exactly what you need, call the 'Cancel Order' automation step from code: Running Automation Step from Code
To call automation step you have to:
Define a new custom PXView that will return record that we want to
process
Create an adapter, that will provide data for button handler. Adapter
will get data from custom PXview.
Create a separate instance of graph, that will handle action.
Code:
public class SOOrderEntry_Extension:PXGraphExtension<SOOrderEntry>
{
//Lets define additional button than will call automation button.
public PXAction<SOOrder> ButtonExample;
[PXButton()]
[PXUIField(DisplayName = "Button Example")]
public virtual IEnumerable buttonExample(PXAdapter adapter)
{
SOOrder order = Base.Document.Current;
//creating a graph that will process Internal command
SOOrderEntry graph = PXGraph.CreateInstance<SOOrderEntry>();
graph.Document.Current = graph.Document.Search<SOOrder.orderNbr>(order.OrderNbr, order.OrderType);
//Searching for correct button from that is defined in Automatin steps.
//All sub menues are adden under action button, so we can get them and iterate.
foreach (var action in (graph.action.GetState(null) as PXButtonState).Menus)
{
if (action.Command == "Cancel Order")
{
//Constructing dummy view that will always return only one record.
adapter = new PXAdapter(new DummyView(graph, graph.Document.View.BqlSelect, new List<object> { order }));
//defining a button command
adapter.Menu = action.Command;
//running button
return graph.action.Press(adapter);
}
}
return adapter.Get();
}
//Defining a dummy view that is inherited from PXView
internal class DummyView : PXView
{
//Storing list of records
List<object> _Records;
internal DummyView(PXGraph graph, BqlCommand command, List<object> records)
: base(graph, true, command)
{
_Records = records;
}
//Everytime when system calls select for the view, retun saved records.
public override List<object> Select(object[] currents, object[] parameters, object[] searches, string[] sortcolumns, bool[] descendings, PXFilterRow[] filters, ref int startRow, int maximumRows, ref int totalRows)
{
return _Records;
}
}
}
I would think it's preferable to call the Automation Step instead of manually setting the Cancelled field because if a user changes the Automation Step you would pick up the changes by calling it.
Note that when possible you should always use existing Graph when you need to manually handle data because this will trigger validations.
If you were to manually change the Cancelled field using SOOrderEntry instead of calling the automation steps, the following validation in SOOrderEntry would still apply:
protected virtual void SOOrder_Cancelled_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
{
SOOrder row = (SOOrder) e.Row;
PXResultset<CCProcTran> trans = PXSelect<CCProcTran, Where<CCProcTran.origRefNbr, Equal<Current<SOOrder.orderNbr>>,
And<CCProcTran.origDocType, Equal<Current<SOOrder.orderType>>,
And<CCProcTran.refNbr, IsNull,
And<CCProcTran.docType, IsNull>>>>>
.Select(this);
CCProcTranHelper.UpdateCCPaymentState(row, trans);
if (row != null && (row.IsCCAuthorized == true || row.IsCCCaptured == true))
{
bool authIsValid = true;
if (row.IsCCAuthorized == true)
{
if (row.CCAuthTranNbr != null)
{
CCProcTran authTran = PXSelect<CCProcTran, Where<CCProcTran.tranNbr, Equal<Required<CCProcTran.tranNbr>>>>.Select(this, row.CCAuthTranNbr);
if (String.IsNullOrEmpty(authTran.DocType) == false && String.IsNullOrEmpty(authTran.RefNbr) == false)
{
authIsValid = false;
}
}
else
{
CCProcTran authTran = this.ccAuthTrans.Select(); //Double-checking for valid auth tran
if (authTran == null)
authIsValid = false;
}
if (authIsValid && row.CCAuthExpirationDate.HasValue)
{
authIsValid = row.CCAuthExpirationDate.Value > PXTimeZoneInfo.Now;
}
}
if (authIsValid)
{
sender.RaiseExceptionHandling<SOOrder.cCPaymentStateDescr>(row, row.CCPaymentStateDescr, new PXSetPropertyException(Messages.CannotCancelCCProcessed, PXErrorLevel.Error));
}
}
}
I am trying to copy some user fields from the CRM Quote to the Sales Order. The CRM Quote uses a different object than the Sales Quote and there doesn't appear to be a way to relate it back. I tried overriding the Create Sales Order to add a handler, but this didn't seem to work Any help would be appreciated. Here is the code I tried:
public class OpportunityMaint_Extension : PXGraphExtension<OpportunityMaint>
{
public delegate IEnumerable CreateSalesOrderDelegate(PXAdapter adapter);
[PXOverride]
public virtual IEnumerable CreateSalesOrder(PXAdapter adapter, CreateSalesOrderDelegate baseMethod)
{
Base.RowInserting.AddHandler<SOLine>((sender, e) =>
{
SOLine orderLine = e.Row as SOLine;
if (orderLine == null) return;
SOLineExt orderLineExt = orderLine.GetExtension<SOLineExt>();
var product = Base.Products.Current;
CROpportunityProductsExt productExt = product.GetExtension<CROpportunityProductsExt>();
orderLineExt.UsrHasAnticipatedDiscount = productExt.UsrHasAnticipatedDiscount;
orderLineExt.UsrAnticipatedDiscountPct = productExt.UsrAnticipatedDiscountPct;
orderLineExt.UsrAnticipatedDiscountAmt = productExt.UsrAnticipatedDiscountAmt;
orderLineExt.UsrAnticipatedUnitPrice = productExt.UsrAnticipatedUnitPrice;
orderLineExt.UsrTotalAnticipatedDiscountAmt = productExt.UsrTotalAnticipatedDiscountAmt;
});
return baseMethod(adapter);
}
}
Thanks!
There are two posts with answers to this same question:
Populate custom field while creating sale order from opportunity
How to pass custom field vales from Opportunity to sales Order?
To sum it up, you can add a rowinserting event handler within the button action or my preference is within DoCreateSalesOrder (extending OpportunityMaint) like the example below...
[PXOverride]
public virtual void DoCreateSalesOrder(OpportunityMaint.CreateSalesOrderFilter param, Action<OpportunityMaint.CreateSalesOrderFilter> del)
{
PXGraph.InstanceCreated.AddHandler<SOOrderEntry>(graph =>
{
graph.RowInserting.AddHandler<SOLine>((cache, args) =>
{
var soLine = (SOLine)args.Row;
if (soLine == null)
{
return;
}
CROpportunityProducts opProduct = PXResult<CROpportunityProducts>.Current;
if (opProduct == null)
{
return;
}
var opProductExt = PXCache<CROpportunityProducts>.GetExtension<CROpportunityProductsExt>(opProduct);
var soLineExt = PXCache<SOLine>.GetExtension<SOLineExt>(soLine);
//Copy all extension fields here...
});
});
del(param);
}
The goal is taking the Journal Transaction generated from the AP Bill page and adding 2 additional rows in GLTran.
1st Attempt
First, I extended the Release action from the Journal Transactions graph to include the 2 new lines:
public class JournalEntryExt : PXGraphExtension<JournalEntry>
{
public delegate IEnumerable ReleaseDelegate(PXAdapter adapter);
[PXOverride]
public IEnumerable Release(PXAdapter adapter, ReleaseDelegate baseMethod)
{
baseMethod(adapter);
//new code
GLTran tranRow = new GLTran();
tranRow = this.Base.GLTranModuleBatNbr.Insert(tranRow);
tranRow.AccountID = 2713;
tranRow.SubID = 467;
tranRow.CuryDebitAmt = 100;
this.Base.GLTranModuleBatNbr.Update(tranRow);
tranRow = new GLTran();
tranRow = this.Base.GLTranModuleBatNbr.Insert(tranRow);
tranRow.AccountID = 1514;
tranRow.SubID = 467;
tranRow.CuryCreditAmt = 100;
this.Base.GLTranModuleBatNbr.Update(tranRow);
this.Base.Actions.PressSave();
return adapter.Get();
}
Result: Creating and releasing the batch, entered the 2 new lines correctly.
After this, I thought that releasing the AP Bill would also trigger this extended logic from the GL Page. However, that didn't occur - The release of the Bill doesn't seem to re-use the Release logic defined in the GL page.
2nd Attempt
Then, I went back to the GL page and included the logic in the RowPersisted event, so that the 2 new lines would get created right after saving the document:
public class JournalEntryExt : PXGraphExtension<JournalEntry>
{
protected virtual void Batch_RowPersisted(PXCache sender, PXRowPersistedEventArgs e)
{
if (e.Row == null)
{
return;
}
Batch batchRow = (Batch)e.Row;
if (batchRow != null
&& e.Operation == PXDBOperation.Insert
&& e.TranStatus == PXTranStatus.Completed)
{
////new code
GLTran tranRow = new GLTran();
tranRow = this.Base.GLTranModuleBatNbr.Insert(tranRow);
tranRow.AccountID = 2713;
tranRow.SubID = 467;
tranRow.CuryDebitAmt = 102;
this.Base.GLTranModuleBatNbr.Update(tranRow);
tranRow = new GLTran();
tranRow = this.Base.GLTranModuleBatNbr.Insert(tranRow);
tranRow.AccountID = 1514;
tranRow.SubID = 467;
tranRow.CuryCreditAmt = 102;
this.Base.GLTranModuleBatNbr.Update(tranRow);
}
}
Result: Creating and saving the Batch correctly entered the 2 new lines.
After this, I thought that releasing the AP Bill would trigger this extended event, given that a Journal Entry graph should get created and used from the Bill page, but in this case, also releasing the AP Bill, did not add the 2 new lines in the generated Batch.
3rd Attempt
Then I thought I could extend the Bill's Release action and take control of the generated Journal Entry with the Search<> method. However, in this case, the extended logic seems to be executed within a transaction as the Document.Current.BatchNbr was still NULL:
4th Attempt
Finally, I tried to extend the Persist() method of APReleaseProcess similarly to how it's done in the guide T300, however none of the methods are listed (version 17.207.0029):
Any other ideas as to how to enter these GL Lines?
Thanks!
Hopefully, it didn't take you forever to go through these 4 attempts... I've got to say though, the number of efforts and details in your question is quite impressive and definitely very appreciated!
A 2-step customization will be required to insert 2 additional GL Transactions in the Batch generated for an AP Bill:
to insert additional GL Transactions, you need to override Persist method within the JournalEntry BLC extension and invoke the logic to insert additional GLTrans only if the custom ModifyBatchFromAP boolean flag value equals True:
using PX.Data;
using System;
namespace PX.Objects.GL
{
public class JournalEntry_Extension : PXGraphExtension<JournalEntry>
{
private bool modifyBatchFromAP = false;
public bool ModifyBatchFromAP
{
get
{
return modifyBatchFromAP;
}
set
{
modifyBatchFromAP = value;
}
}
[PXOverride]
public void Persist(Action del)
{
if (ModifyBatchFromAP)
{
var glTran = Base.GLTranModuleBatNbr.Insert();
Base.GLTranModuleBatNbr.SetValueExt<GLTran.accountID>(glTran, "20000");
glTran = Base.GLTranModuleBatNbr.Update(glTran);
Base.GLTranModuleBatNbr.SetValueExt<GLTran.subID>(glTran, "000000");
glTran.CuryDebitAmt = 100;
glTran.TranDesc = "Additional Debit Transaction for AP Doc";
Base.GLTranModuleBatNbr.Update(glTran);
glTran = Base.GLTranModuleBatNbr.Insert();
Base.GLTranModuleBatNbr.SetValueExt<GLTran.accountID>(glTran, "20200");
glTran = Base.GLTranModuleBatNbr.Update(glTran);
Base.GLTranModuleBatNbr.SetValueExt<GLTran.subID>(glTran, "000000");
glTran.CuryCreditAmt = 100;
glTran.TranDesc = "Additional Credit Transaction for AP Doc";
Base.GLTranModuleBatNbr.Update(glTran);
}
del();
}
}
}
after that in the overridden Release action within the APInvoiceEntry_Extension, you will subscribe to the InstanceCreated event for the JournalEntry BLC type to set ModifyBatchFromAP flag value to True allowing your logic from step 1 to execute for the Batch generated only for an AP document:
using PX.Data;
using PX.Objects.GL;
using System.Collections;
namespace PX.Objects.AP
{
public class APInvoiceEntry_Extension : PXGraphExtension<APInvoiceEntry>
{
public delegate IEnumerable ReleaseDelegate(PXAdapter adapter);
[PXOverride]
public IEnumerable Release(PXAdapter adapter, ReleaseDelegate baseMethod)
{
PXGraph.InstanceCreated.AddHandler<JournalEntry>((JournalEntry graph) =>
{
graph.GetExtension<JournalEntry_Extension>().ModifyBatchFromAP = true;
});
return baseMethod(adapter);
}
}
}
P.S. it is not currently possible to use the Select Method to Override dialog with the APReleaseProcess class due to PXHiddenAttribute applied to it. Let me forward this to our Engineering Team to bring their attention on that matter.