gone through a variety of the articles, and none seem to "Work". In particular, 2021r1, not getting ANY of the PXTrace statement in the output
Challenge:
Static method of the graph directly called from ARPaymentEntry, and 4-5 overloads (From .\App_Data\CodeRepository\PX.Objects\AR\ARDocumentRelease.cs)
Eventually ends up calling a single method within that graph, starting with "public static void ReleaseDoc("
No clear place to add a delegate, though that seems the "most correct" method (E.g. question 37262565, comment from cbetabeta) - Yet the initialize event doesn't appear to be firing (possibly JIT optimization? Direct call into static method doesn't really need the class to be instantiated, I'd guess)
Also need a complete solution - e.g. Must handle the call from Payment Entry as well as from AR Document Release process
Sample Code:
using PX.Data;
using PX.Objects.AR;
using PX.Objects.GL;
using System.Collections;
using System.Collections.Generic;
using static PX.Objects.AR.ARDocumentRelease;
namespace Test.GraphExtensions
{
public class ARDocumentReleaseTestABC : PXGraphExtension<ARDocumentRelease>
{
// Tries include:
// https://html.developreference.com/article/11055300/How+to+customize+the+Process+button+on+the+AP505200+screen.+Acumatica
// https://stackoverflow.com/questions/36784480/customize-release-ap-document-in-acumatica-system
// https://stackoverflow.com/questions/37262565/how-can-i-execute-code-from-the-release-release-all-buttons-in-the-release-ar
// https://living-sun.com/es/acumatica/2179-extend-arpaymententry-release-action-acumatica.html
#region IsActive - Turn off if no setup record
public static bool IsActive()
{
return true;
}
#endregion IsActive - Turn off if no setup record
public virtual void BalancedARDocument_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", "dc1703c7-f8b7-4ce1-b838-d51475f4d477"));
}
public override void Initialize()
{
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", "8ecc4f83-9ac8-4bb6-bad7-ac2aabc5b58e"));
}
public static void ReleaseDocRBRR(ARRegister ardoc, bool isAborted)
{
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} {2}", "20ae0d5e-44eb-42f2-ad15-0b9e307d2a86", isAborted, (ardoc == null)));
}
[PXUIField(DisplayName = "Release", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)]
[PXProcessButton]
// [PXOverride]
public virtual IEnumerable Release(PXAdapter adapter)
{
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", "0ccff0b7-7702-4083-b717-17b031e27be5"));
List<ARRegister> list = new List<ARRegister>();
return list;
}
}
}
The proper place to override is either ARReleaseProcess.OnBeforeRelease if you need your code to fire before the release code or ARReleaseProcess.ReleaseDocProc.
Also, if you just need to run your code after the release, it is recommended that you add your code to onreleasecomplete Delegate in ARReleaseProcess.ReleaseDocProc.
Also, if you just need to run your code after the document release and you do not necessarily need to have it run exactly in the same transaction, please consider adding a separate process that picks up released documents and processes them further. You can either schedule that process ti run in the backgroud every minute or put a trigger with Business Events.
I think its the namespace that is the issue:
namespace PX.Objects.AR
{
public class ARDocumentRelease_Extension : PXGraphExtension<ARDocumentRelease>
{
#region Event Handlers
public override void Initialize()
{
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", "8ecc4f83-9ac8-4bb6-bad7-ac2aabc5b58e"));
}
#endregion
}
}
Seems I had a variety of issues, in particular was an extension to the wrong graph. Hooking up with Dmitrii's answer, I indicate at least some of the events that are hit, so others can use in the future
Code modified per #dmitrii-naumov's answer:
using PX.Data;
using PX.Objects.AR;
using PX.Objects.GL;
using System.Collections.Generic;
namespace Test.ABCD
{
public class ARDocumentReleaseTestABCD : PXGraphExtension<ARReleaseProcess>
{
#region IsActive - Turn off if no setup record
public static bool IsActive()
{
return true;
}
#endregion IsActive - Turn off if no setup record
public override void Initialize()
{
// Hit
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", "8ecc4f83-9ac8-4bb6-bad7-ac2aabc5b58e"));
}
public delegate List<ARRegister> ReleaseDocProcDelegate(JournalEntry je, ARRegister ardoc, List<Batch> pmBatchList, ARDocumentRelease.ARMassProcessReleaseTransactionScopeDelegate onreleasecomplete);
[PXOverride]
public List<ARRegister> ReleaseDocProc(JournalEntry je, ARRegister ardoc, List<Batch> pmBatchList, ARDocumentRelease.ARMassProcessReleaseTransactionScopeDelegate onreleasecomplete, ReleaseDocProcDelegate del)
{
// Hit
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", "8f153b0b-dd57-4893-aa8c-d29ea69528e4"));
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", "9b1caa30-5abf-4f21-8c45-a55c4047441a"));
return null;
}
[PXOverride]
public virtual ARRegister OnBeforeRelease(ARRegister ardoc)
{
// Hit
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", "741a01ad-5a37-40a8-ad10-a1fcd6659f7e"));
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", "9b1caa30-5abf-4f21-8c45-a55c4047441a"));
return ardoc;
}
}
}
This version implements OnReleaseComplete, from code Dmitrii provided to me (I'll take the blame for mistakes, but he got the event hooked up correctly, so do get the OnComplete event displayed in the log):
public class ARDocumentReleaseTESTABC : PXGraphExtension<ARReleaseProcess>
{
#region IsActive
public static bool IsActive()
{
return true;
}
#endregion IsActive
public void OnReleaseComplete(ARRegister doc)
{
///code
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", "7d22cef8-c14d-4111-bcc5-405be006ea6b"));
}
#region ReleaseDocProc
public delegate List<ARRegister> ReleaseDocProcDelegate(JournalEntry je, ARRegister ardoc, List<Batch> pmBatchList, ARDocumentRelease.ARMassProcessReleaseTransactionScopeDelegate onreleasecomplete);
[PXOverride]
public List<ARRegister> ReleaseDocProc(JournalEntry je, ARRegister ardoc, List<Batch> pmBatchList, ARDocumentRelease.ARMassProcessReleaseTransactionScopeDelegate onreleasecomplete, ReleaseDocProcDelegate del)
{
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", "d508b521-976c-452b-9765-57f532a0a513"));
onreleasecomplete += OnReleaseComplete;
PXTrace.WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", "309e2e6b-6fb8-4c75-ab8a-40e56c173562"));
return del(je, ardoc, pmBatchList, onreleasecomplete);
}
#endregion
}
Related
I'd like to add some custom code to the process of the Release Payments screen. If I create a customization project and add a Graph extension to the CODE section, I can't find any method with a name similar to 'Release Payments' to override in the list to select from (even though it exists as public in the BLC):
and if I try to manually code the override, I get an error that it can't find that method.
So basically, my question is, how do I intercept the process on the Release Payments screen?
I have had success with overriding the ARReleaseProcess.ReleaseDocProc like below. All the AR documents (invoices, payments, etc.) call the ARReleaseProcess.ReleaseDocProc during the release and that function does the main process. The below code is basically calling what that function was supposed to do and adding the delegate for when the release is completed.
public class ARReleaseProcessExt: PXGraphExtension<ARReleaseProcess>
{
public static bool IsActive()
{
return true;
}
public void OnReleaseComplete(ARRegister doc)
{
try
{
if (doc!=null && doc.DocType== ARDocType.Payment)
{
///ADD your code here
}
}
catch(Exception exc)
{
/// handler errors.
}
}
#region ReleaseDocProc
public delegate List<ARRegister> ReleaseDocProcDelegate(JournalEntry je, ARRegister ardoc, List<Batch> pmBatchList, ARDocumentRelease.ARMassProcessReleaseTransactionScopeDelegate onreleasecomplete);
[PXOverride]
public List<ARRegister> ReleaseDocProc(JournalEntry je, ARRegister ardoc, List<Batch> pmBatchList, ARDocumentRelease.ARMassProcessReleaseTransactionScopeDelegate onreleasecomplete, ReleaseDocProcDelegate del)
{
onreleasecomplete += OnReleaseComplete;
return del(je, ardoc, pmBatchList, onreleasecomplete);
}
#endregion
}
The override is a little different in case of AP module as the signature of the function is different there.
public class APReleaseProcess_Extension : PXGraphExtension<PX.Objects.AP.APReleaseProcess>
{
#region Event Handlers
public delegate List<APRegister> ReleaseDocProcDelegate(JournalEntry je, APRegister doc, Boolean isPrebooking, ref List<INRegister> inDocs);
[PXOverride]
public List<APRegister> ReleaseDocProc(JournalEntry je, APRegister doc, Boolean isPrebooking, ref List<INRegister> inDocs, ReleaseDocProcDelegate baseMethod)
{
var retVal = baseMethod(je,doc,isPrebooking,ref inDocs);
//Your code goes here.
PXTrace.WriteInformation("My override worked!");
return retVal;
}
#endregion
}
Good day
What is the correct way to change the below Release function to: first do the base function and only do your code?
When you use the built-in Acumatice "Override method"(that can be found when editing code) you get the following:
namespace PX.Objects.AR
{
public class ARPaymentEntry_Extension : PXGraphExtension<ARPaymentEntry>
{
#region Event Handlers
public delegate IEnumerable ReleaseDelegate(PXAdapter adapter);
[PXOverride]
public IEnumerable Release(PXAdapter adapter, ReleaseDelegate baseMethod)
{
//Normally you will add your code here.
return baseMethod(adapter);
}
#endregion
}
}
If you wanted the base to run fist and then you wanted your code after if would somthing like this:
namespace PX.Objects.AR
{
// Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
public class ARPaymentEntry_Extension : PXGraphExtension<ARPaymentEntry>
{
#region Event Handlers
public delegate IEnumerable ReleaseDelegate(PXAdapter adapter);
[PXOverride]
public IEnumerable Release(PXAdapter adapter, ReleaseDelegate baseMethod)
{
baseMethod(adapter);
// your code here.
return;
}
#endregion
}
}
Question: What should I put as the return?
If I use null it gives a return nextstep error
public IEnumerable Release(PXAdapter adapter, ReleaseDelegate baseMethod)
{
IEnumerable result = baseMethod(adapter);
// your code here
return result;
}
Good day
I am trying to call the ApplyState(state) function. This is a protected function inside PX.Objects.IN.INScanReceive.
I am reading a QR code and the value has a lot/serial number and expiry date. The idea is to set the 2 values during the ProcessItemBarcode function call. The problem is when calling the base method the state is set and the "screens state" wants me to scan the lot/serial.
What I need to do is call the ProcessConfirm() or ApplyState(state). But calling the functions inside ProcessItemBarcode is where I stuck:
using PX.Common;
using PX.Data;
using WMSBase = PX.Objects.IN.WarehouseManagementSystemGraph<PX.Objects.IN.INScanReceive, PX.Objects.IN.INScanReceiveHost, PX.Objects.IN.INRegister, PX.Objects.IN.INScanReceive.Header>;
using PX.Objects.IN;
namespace MyCustomPackage.Graph.Extension
{
public class INScanReceiveHostExtCustomPackage2 : PXGraphExtension<INScanReceive, INScanReceiveHost>
{
public static bool IsActive() => true;
#region Overrides ProcessItemBarcode
//ProcessItemBarcode
public delegate void ProcessItemBarcodeDelegate(string barcode);
[PXOverride]
public virtual void ProcessItemBarcode(string barcode, ProcessItemBarcodeDelegate baseMethod)
{
//..//logic go change barcode
baseMethod?.Invoke(barcode);
// Option one call ApplyState()
Base.ApplyState()
// Option2 one call ProcessLotSerialBarcode
// I dont know how to send the delegate
ProcessLotSerialBarcode(barcode, );
}
#endregion
#region Overrides ProcessLotSerialBarcode
//ProcessLotSerialBarcode
public delegate void ProcessLotSerialBarcodeDelegate(string barcode);
[PXOverride]
public virtual void ProcessLotSerialBarcode(string barcode, ProcessLotSerialBarcodeDelegate baseMethod)
{
baseMethod?.Invoke(barcode);
}
#endregion
[PXProtectedAccess]
public abstract class INScanReceiveHostExtProtectedAccess : PXGraphExtension<INScanReceiveHostExtCustomPackage, INScanReceive, INScanReceiveHost>
{
[PXProtectedAccess(typeof(INScanReceive))]
protected abstract void ProcessItemBarcode(string barcode);
[PXProtectedAccess(typeof(INScanReceive))]
protected abstract void ApplyState(string state);
[PXProtectedAccess(typeof(INScanReceive))]
protected abstract void ProcessLotSerialBarcode(string barcode);
}
}
}
This is not the preferred method but when I can't figure out another option I will copy the source from Acumatica, e.g. Static methods and private ones. I will make note in my code that they were copied so I can make sure to duplication the process when versions change. You can then call those methods locally. Not elegant but works for me :)
Good day
I am looking for a way to override the function ProcessItemBarcode(string Barcode) inside public class INScanReceive : WMSBase
The idea is to extend the INScanReceiveHost graph and manipulate the barcode before it is processed. This is to change how the Scan and Receive page scan'sa barcodes so that I can read and manipulate QR codes
namespace PX.Objects.IN
{
// Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
public class INScanReceiveHost_Extension : PXGraphExtension<INScanReceive>
{
#region Event Handlers
[PXOverride]
public virtual void ProcessItemBarcode(string barcode)
{
//change barcode
Base.ProcessItemBarcode.Invoke(barcode);
}
#endregion
}
}
But It telling me i can't access the function?
Update 2021/06/15
Thanks Sean Prouty for your help so far. I am getting very close to a solution.
I have a follow-up question.
I have overridden the ProcessItemBarcode function and ProcessLotSerialBarcode
Question: Can I call ProcessLotSerialBarcode from ProcessItemBarcode
because I have the location from the QR code I can set it from the first scan:
#region Overrides ProcessItemBarcode
//ProcessItemBarcode
public delegate void ProcessItemBarcodeDelegate(string barcode);
[PXOverride]
public virtual void ProcessItemBarcode(string barcode, ProcessItemBarcodeDelegate baseMethod)
{
try
{
string inventoryBC = barcode;
//...//get InvetoryID using Barcode
baseMethod?.Invoke(inventoryBC);
//how do you call the ProcessLotSerialBarcode function?
ProcessLotSerialBarcode(barcode, ProcessLotSerialBarcodeDelegate);
}
catch (Exception ex)
{//TODO: check if not a QR code
PXTrace.WriteError("ProcessItemBarcode Override: " + ex.Message);
baseMethod?.Invoke(barcode);
}
}
#endregion
#region Overrides ProcessLotSerialBarcode
//ProcessLotSerialBarcode
public delegate void ProcessLotSerialBarcodeDelegate(string barcode);
[PXOverride]
public virtual void ProcessLotSerialBarcode(string barcode, ProcessLotSerialBarcodeDelegate baseMethod)
{
try
{
string inventoryBC = "LOgic";
baseMethod?.Invoke(inventoryBC);
}
catch (Exception)
{
//TODO: check if not a QR code
baseMethod?.Invoke(barcode);
}
}
#endregion
[PXProtectedAccess]
public abstract class INScanReceiveHostExtProtectedAccess : PXGraphExtension<INScanReceiveHostExtCustomPackage, INScanReceive, INScanReceiveHost>
{
[PXProtectedAccess(typeof(INScanReceive))]
protected abstract void ProcessItemBarcode(string barcode);
[PXProtectedAccess(typeof(INScanReceive))]
protected abstract void ApplyState(string state);
[PXProtectedAccess(typeof(INScanReceive))]
protected abstract void ProcessLotSerialBarcode(string barcode);
}
I was also thinking of setting the state from the first method but then I need to call
Base.ApplyState(INScanIssue.ScanStates.Confirm);
Then I can set the Header and just keep resetting the scanner to the Confirm state. What do you think?
Because the method you are trying to override is protected, and not public, you will need to override the logic in a different way using the PXProtectedAccess attribute and an abstract graph extension.
namespace MyCustomPackage.Graph.Extension
{
public class INScanReceiveHostExtCustomPackage : PXGraphExtension<INScanReceive, INScanReceiveHost>
{
public static bool IsActive() => true;
#region Overrides
public delegate void ProcessItemBarcodeDelegate(string barcode);
[PXOverride]
public virtual void ProcessItemBarcode(string barcode, ProcessItemBarcodeDelegate baseMethod)
{
PXTrace.WriteInformation("Running abstract override");
baseMethod?.Invoke(barcode);
}
#endregion
}
[PXProtectedAccess]
public abstract class INScanReceiveHostExtProtectedAccess : PXGraphExtension<INScanReceiveHostExtCustomPackage, INScanReceive, INScanReceiveHost>
{
[PXProtectedAccess(typeof(INScanReceive))]
protected abstract void ProcessItemBarcode(string barcode);
}
}
I didn't have a good way to test this unfortunately, so you may need to tweak the type that is being passed to the PXProtectedAccess attribute above the abstract method. If this doesn't work, try passing the INScanReceiveHost type to the attribute and see if that works for you.
I am trying to customize the Release Cash Transactions screen and I am using this way to be able to call the Persist method, however when I release a record it does not enter the method specified above.
Here I am showing my code that if it works correctly for AP and AR.
I am doing the same for CA.
Could you help me that I'm wrong. Thanks for your help beforehand.
public class CAReleaseProcess_Extension : PXGraphExtension<CAReleaseProcess>
{
#region Custom
private WeakReference<JournalEntry> je;
public override void Initialize()
{
base.Initialize();
PXGraph.InstanceCreated.AddHandler<JournalEntry>(delegate (JournalEntry graph)
{
je = new WeakReference<JournalEntry>(graph);
});
}
public delegate void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
CASplit doc = Base.CASplits.Current;
ActualizarCATaxTran(doc);
baseMethod();
}
public virtual void ActualizarCATaxTran(CASplit doc)
{
foreach (CATaxTran iTaxTran in Base.CATaxTran_TranType_RefNbr.Select(doc.AdjTranType, doc.AdjRefNbr))
{
if (iTaxTran != null)
{
//do something
}
}
}
#endregion
}
CA502000