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.

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.

 

 

How To Get User Friendly Error Message Out Of Webexception

 

Hello everybody,

today I want to leave a short notice on how to get user friendly text of WebException. Imagine that you need to know which part of data in your json is missing. How can you quickly figure out which part? For one of my projects I wanted to get easy way of saying user which field is potentially missing. In order to achieve that I've used following fragment:

catch (WebException ex)
{
  string errorMessage = string.Empty;
  if (ex.Response != null)
  {
      using (var errorResponse = (HttpWebResponseex.Response)
      {
          using (var reader = new StreamReader(errorResponse.GetResponseStream()))
          {
              errorMessage = reader.ReadToEnd();
          }
      }
  }

After that I've just used to throw exception with adding additional details over Data property of Exception class.

 

 

Why Code Changes From Dll Are Not Shown On The Form In Acumatica

 

Hello everybody,

today I want to share with you interesting use case which stolen one night of sleep from me, as well as from one of my collegues. 

We had very trivial case. Or at least we thought it is trivial. In Acumatica we had something like this:

#region UsrCardTheme
[PXDBString(256)]
[PXUIField(DisplayName="Card Theme")]
 
public virtual string UsrCardTheme { getset; }
public abstract class usrCardTheme : PX.Data.BQL.BqlString.Field<usrCardTheme> { }
#endregion

For some reason we weren't able to update it in database, so we've made changes to it like this in order to check if we will see it in UI:

#region UsrCardTheme
[PXDBString(256)]
[PXUIField(DisplayName="Card Theme2")]
 
public virtual string UsrCardTheme { getset; }
public abstract class usrCardTheme : PX.Data.BQL.BqlString.Field<usrCardTheme> { }
#endregion

went to the page, open it, and .... label of column remained the same. We've did our best in order to find what is the problem, what's wrong with the syntax, removed completely, restarted IIS, thrown away garbage, nothing worked. 

Until we've found stackoverflow advice to take a look at folder App_RuntimeCode:

and only after we've cleared up folder App_RuntimeCode we've noticed dream of all night, 2 in the end!

 

How To Call Non Public Method Of Acumatica

 

Hello everybody,

today I want to share with you how it's possible to call some methods of Acumatica, which are not public, and which you don't want to copy/paste completely into your source code. In that case reflection will save you. Consider calling of InsertSOAdjustments method of graph SOOrderEntry below.

MethodInfo invokeSOAdjustment = typeof(SOOrderEntry).GetMethod(
    "InsertSOAdjustments"BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder,
    new[] { typeof(SOOrder), typeof(ARPaymentEntry), typeof(ARPayment) }, null);
 
invokeSOAdjustment.Invoke(Base, new object[] { orderdocgraphpayment });

Also I want to give you a word of warning, that such approach potentially will not be certified, and another way of usage will be the one below:

In extension of SOOrderEntry create lines like those:

[PXOverride]
public void InsertSOAdjustments(SOOrder orderARPaymentEntry docgraphARPayment payment,
    Action<SOOrderARPaymentEntryARPaymentbaseAction)
{
    baseAction(orderdocgraphpayment);
}

and then just call InsertSOAdjustments method whenever you'll have a need for this.

Summary

Because Acumatica is written with C# which is very powerful language which gives you a lot of features you can easily achieve a lot of thigs, also be careful with usage of reflection. Somtime even more then Acumatica team anticipated themselves.

How To Avoid Navigation Away From Created Item After Calling Presssave Or Persist Action

 

Hello everybody,

today I want to tell you a story, that swallowed quite big amount of time of whole team.

Recently we've got seemingly easy to fulfil requirement: 

  1. Add button to the grid
  2. Inside of the button fullfil 
    1. Persist
    2. Call to db with modifications
    3. Persist one more time
  3. Leave the page opened on created item in UI

Initially our code looked like this:

public PXAction<SOOrder> SomeAction;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Some Action", Visible = true)]
protected virtual IEnumerable someAction(PXAdapter adapter)
{
    Base.Actions.PressSave();
    //API call
    Base.Actions.PressSave();
    return adapter.Get();
}

I could say that everything worked perfectly except one tiny detail: after clicking of the button page was navigated away to creation of new Sales order. After plenty of research inside of Acumatica source code we have found this option:

public PXAction<SOOrder> SomeAction;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Some Action", Visible = true)]
protected virtual IEnumerable someAction(PXAdapter adapter)
{
    Base.Actions.PressSave();
    List<SOOrderresult = new List<SOOrder>();
    //API Call
    result.Add(Base.CurrentDocument.Current);
    return result;
}

Another way of dealing with this bug is this:

public PXAction<SOOrder> SomeAction1;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Some Action", Visible = true)]
protected virtual IEnumerable btnCreatingNew(PXAdapter adapter)
{
    Base.Actions.PressSave();
    //some other code
    adapter.Searches[adapter.Searches.Length - 1] = Base.CurrentDocument.Current.RefNbr;
    return adapter.Get();
}

Summary

Reason of such behavior is fact that Acumatica uses value returned from action in order to know which order to open. Then happens this:

  1. on the first line of the code your adapter has information that Acumatica should go to <New> sales order
  2. Base.Actions.PressSave() generates sales order and persists it to Db, but doesn't notify adapter about this fact
  3. When adapter.Get is executed, Acumatica reads from it order which should be opened, finds there <New> and navigates away from created SO

In order to deal with you can choose option 1, or option 2. Option 1 I've discovered in Acumatica source code, and option 2 is mentioned at stackoverflow by Ruslan

 

 

 

 

Template For Usage Of Pxlinenbr Attribute In Acumatica

 

Hello everybody,

today I want to leave a note on how to use PXLineNbr attribute in Acumatica pages. Recently myself and one of my team members struggled a bit with a question on how to add it properly. Below goes a bit changed worked tempalte:

public class PrimaryTable : IBqlTable
{
    #region LineId
    public abstract class lineId : PX.Data.BQL.BqlInt.Field<lineId> { }
 
    [PXDBInt]
    [PXDefault(0)]
    public virtual int? LineId { getset; }
    #endregion
 
    #region LineCntr
    public abstract class lineCntr : PX.Data.BQL.BqlInt.Field<lineCntr> { }
 
    [PXDBInt]
    [PXDefault(0)]
    public virtual int? LineCntr { getset; }
    #endregion
}
 
public class DetailsTable : IBqlTable
{
    #region LineNbr
    public abstract class lineNbr : IBqlField { }
 
    [PXDBInt(IsKey = true)]
    [PXDefault]
    [PXLineNbr(typeof(PrimaryTable.lineCntr))]
    [PXParent(typeof(SelectFrom<PrimaryTable>.Where<PrimaryTable.lineId.IsEqual<PrimaryTable.lineId
        .FromCurrent>>))]
    public virtual int? LineNbr { getset; }
    #endregion
}

Summary

As you see, nothing special in this code. Add PXParent, PXLineNbr and enjoy with line counter feature.