How to override ReleaseReceipt method in Acumatica

Hi everybody,

today I want to share with you how you can override base method ReleaseReceipt. One of the ways of achieving this is to re-use Action.

For example like this in class, which inherits from POReceiptEntry:

 

[PXOverride]
public void ReleaseReceipt(INReceiptEntry docgraph, AP.APInvoiceEntry invoiceGraph, POReceipt aDoc,
    DocumentList<INRegister> aINCreated, DocumentList<AP.APInvoice> aAPCreatedbool aIsMassProcess,
    Action<INReceiptEntry, AP.APInvoiceEntry, POReceipt,
        DocumentList<INRegister>, DocumentList<AP.APInvoice>, boolreleaseBase
)
{
    // Here you can add your logic
    releaseBase(docgraph, invoiceGraph, aDoc, aINCreated, aAPCreated, aIsMassProcess);
// as well as here }

With such approach you'll be able to save a bit of coding with delegate, and also prepend some steps before release execution, and append after release execution.

 

How to find PXProjection which has SOOrder in the next line in Acumatica source code

Hello everybody,

today I want to speak about very useful feature in Visual Studio.

Sometime you may need some kind of source of inspiration from Acumatica source code. But quite often that source of inspiration have text, which is scattered over multiple lines of code.

For example, you want to find file which has PXProjection text in one line, and word SOOrder in the next line. How to make such a search? Window below appears once you click on Ctrl + Shift + F:

with help of .*\r?\n.* you can make search over multiple files. Take a note of what Visual Studio showed to me in output results once I've clicked on Find All:

and then, you can double click on any of those lines, and make sure, that you found something, that is PXProjection, with SOOrder in some of the next lines:

Summary

With such simple technique you can easily hunt for any lines of code in Acumatica framework, and enhance your search results.

 

 

A DAC extension must include the publis static IsActive method

Hello everybody,

today  I want to share one line of code for Acuminator for error message:

PX1016 A DAC extension must include the public static IsActive method with the bool return type. Extensions which are constantly active reduce performance. Suppress the error if you need the DAC extension to be constantly active.

In case if you don't want to suppress Acuminator with a comment, you can do something like this inside of your extension:

public static bool IsActive() => true;

Certainly it is not the most elegant way of doing that, as better way could be usage of some attribute for this purpose, or for example use inheritance, but as of now, the smallest amount of code, you can use that line of code which is presented here. 

 

Purpose of RowPersisting event

Hello everybody,

today I want to leave a note on usage of RowPersisting event.

Quite often I see situations, when RowPersisting is used for making additional insertions to database. Also quite often I see cases when some additional inserts being performed to database. 

I want to warn against such an approach. Reason for that is that during RowPersisting event, Acumatica opens transaction scope. Because of that, additional readings from db, or additional persists to db in scope of RowPersisting may lead to performance degradation and even deadlocks. 

Purpose of RowPersisting event is kind of latest resort, in which you can modify your record before putting it to database. And it shouldn't be used for some other purposes. Other purposes of RowPersisting event is validate record before it was putted to database, or cancel commit operation through throwing of an exception.

 

 

 

 

 

Lightweight persist to database

Hello everybody,

today I want to describe following use case. Quite often it is needed to persist to database one or another DAC class, which is filled by some data. 

As usually I see people do this via hard coding of DAC class inside of the Graph. But today I want to share with you a way of persisting DAC class without hardcoding it as a view. 

In order to accomplish this, you can use following graph:

public class ImportEntitiesInsertion : PXGraph<ImportEntitiesInsertion>
{
    public string AddView(Type dacType)
    {
        var viewName = "_DYNAMIC_" + dacType.GetLongName();
        if (!this.Views.ContainsKey(viewName))
        {
            var command = BqlCommand.CreateInstance(typeof(Select<>), dacType);
            var newView = new PXView(thistrue, command);
            Views.Add(viewName, newView);
            Views.Caches.Add(dacType);
        }
        return viewName;
    }
}

 

After that, in some other place of the code, you can use this graph like this:

 

var graphForInsertion = PXGraph.CreateInstance<ImportEntitiesInsertion>();
var dacType = typeof(SOOrder); 
var viewName = graphForInsertion.AddView(dacType);
 
for (int i = 0; i < 10; i++)
{
    var newOrd = new SOOrder();
    graphForInsertion.Views[viewName].Cache.Insert(newOrd);
}
graphForInsertion.Persist();

 

 What I especially like about this approach, is that records will be persisted initially in the cache, and only after you'll call Persist, all bunch of records will be persisted to database.

 

 

How to override properly CreateMatrixItems

Hello everybody,

today I want to leave a short snippet on how to override methods in CreateMatrixItemsImpl graph extension. Below goes code snippet you can use for this purpose:

 

public class CreateMatrixItemsImplExt : PXGraphExtension<CreateMatrixItems.CreateMatrixItemsImpl, CreateMatrixItems>
{
    public override void Initialize()
    {
        base.Initialize();
    }
}

With help of this code fragment you can override and customize a bit more Matrix management of Acumatica.

FUNCTION pp_conv2smallInt does not exist while importing database for MYOB and Acumatica

Hello everybody,

today I want to share with you one rake, which stolen from me few days of my life. 

Recently I imported SQL backup of MySQL database, and got error like this:

17:20:58 Restoring D:\Backups\Wire\rev.sql
Running: mysql.exe --defaults-file="c:\users\zalju\appdata\local\temp\tmp5dndjm.cnf" --protocol=tcp --host=localhost --user=root --port=3306 --default-character-set=utf8 --comments --database=rev < "D:\\Backups\\Wire\\rev.sql"
ERROR 1305 (42000) at line 70858: FUNCTION rev.pp_conv2smallInt does not exist

Operation failed with exitcode 1
17:28:28 Import of D:\Backups\\Wire\rev.sql has finished with 1 errors

Error message looked similar to what you can see below:

After plenty of googling and applying different advice I was disappointed as nothing worked for me. 

Then I've decided to take a look on clean database of installed Acumatica and discovered the following:

Then I've decided to create such a function manually and tried to execute import one more time. 

Execution lasted for a bit longer period of time, but now I got another error message, but now related to function pp_conv2smallInt, which you should be aware of how to fix.

Summary

If you make import of MySQL database, then prior to it create functions pp_conf2int, binaryMaskTest and other standard Acumatica functions, otherwise you'll get error messages similar to mine, and anyway will need to create them properly. Not very much convenient but working approach. In other words, cheap comes with it's price.

 

 

 

 

How to make selector for CSAnswers

Hello everybody,

recently one of the colleagues asked me how to make selector from Attributes values. Also that request seem trivial, but still took some time, especially with usage of FBQL query to build. 

Below goes template you may use if you'll need some kind of selector for attributes by some predefined value:

 

public class SOOrderExt : PXCacheExtension<SOOrder>
    {
        public class Codes
        {
            public const string MediaCode = "MEDIACODE";

            public class mEdiaCode : BqlType<IBqlStringstring>.Constant<mEdiaCode>
            {
                public mEdiaCode() : base("mEdiaCode")
                {
                }
            }

            public const string OrdOrigin = "ORDORIGIN";

            public class ordOrigin : BqlType<IBqlStringstring>.Constant<ordOrigin>
            {
                public ordOrigin() : base("ORDORIGIN")
                {
                }
            }
        }

        [PXSelector(typeof(SearchFor<CSAnswers.value>.Where<CSAnswers.attributeID.IsEqual<Codes.ordOrigin>>))]
        [PXDBString(50)]
        public string SomeValue1 { getset; }

        [PXSelector(typeof(SearchFor<CSAnswers.value>.Where<CSAnswers.attributeID.IsEqual<Codes.mEdiaCode>>))]
        [PXDBString(50)]
        public string SomeValue { getset; }
    }

 

 For me it was also interesting to note, that in the past it was common to use for Selector combination of PXSelector with Search, but in FBQL you'll need PXSelector with SearchFor.

How to use Const in FBQL for Acumaitca

Hello everybody,

I want to leave a quick hint on how to use Const values in Acumatica for FBQL. Below goes sample:

public class someBranch : PX.Data.BQL.BqlInt.Constant<someBranch>
{
    public someBranch() : base(48)
    {
    }
}

Then later on you can use it in your BQL and FBQL queries for filtering

How to use PXLongOperation

Hello everybody,

Today I want to write a few words on usage of PXLongOperation. 

Compare two following scenarios:

Base.Save.Press();
            try
            {
                PXLongOperation.StartOperation(Base,  ()=>
                {
                    //Some other code
                    Base.Save.Press();

 

with this:

Base.Save.Press();
 
var doc = Base.Document.Current;
var orderType = doc.OrderType;
var orderNbr = doc.OrderNbr;
 
try
{
    PXLongOperation.StartOperation(Base,  ()=>
    {
        var grp = PXGraph.CreateInstance<SOOrderEntry>();
        grp.Document.Current = grp.Document.Search<SOOrder.orderNbr>(orderNbr, orderType);
 
        grp.Save.Press();
    });

and tell me what will be the difference in execution of those two types of code?

I spent pretty big amount of time wondering why in Acumatica source code I often seen scenario #2. Reason why I was puzzled is that I don't like to create instance of something, if I can use some variable that exists already. 

And finally I've discovered reason on why scenario #2 is preferable. After our team spent some time on digging on the following use case scenario. We've used scenario #1 and QA gave us very interesting bug: some buttons on UI level got disabled after execution of #1 scenario. The only way to enable them in scenario #1 was just to call refresh of the page:

throw new PXRedirectRequiredException(Base, false"Sales Orders");

which is not the worst in life of end user, but definetly not the most convenient. How to avoid total refresh of the screen? Use scenario #2. 

Another important aspec of scenario #2 is usage of variables. Take note, that inside of PXLongOperation I don't use Base.Document.Current.OrderType. Instead I use local variables doc, orderType and orderNbr which is then used at async thread. 

Summary

Starting from today I plan to use #2 whenever I will deal with multithreading scenarions. Otherwise some UI problems will become some kind of guarantee.