Failed To Subscribe Event Cacheattached In Graph

Failed to subscribe event CacheAttached in graph

Hello everybody, who follows my blog.

Today I want to share with you some surpsise of Acumatica which taken me some time. 

In one of my graphs I added CacheAttached event in order to modify way of displaying title. As outcome I got something like this:

Failed to subscribe event Graph::Table_Row_CacheAttached in graph Graph. 
Method signature looks like event handler, 
but cache Table has not been found in the list of auto initialized caches.
Remove unused event handlers from code.

Let's assume that Table name is BAccount. 

It is possible to solve with the following statement:

[PXHidden]
public PXSelect<BAccount> HiddenBacount;

The following statement added BAccount into list of auto initalized caches and solved my problem.

2 Comments

  • Fabio said

    Hi.
    Today I had similar problems, and thanks to your site I solved issue

  • docotor said

    PXHidden does the following: hides the data access class (DAC), the business logic controller (graph), or the view from the selectors of DACs and graphs and from the Web Service API clients.

Add a Comment

11 Lessons You Will Never Learn In School

 

Hello Everybody,

I want to share what I found here some ideas:

RULE 1: Life’s not fair. Get used to it

You could be the smartest, hardest working most noble person alive and still not get that A. Or promotion. Or the girl, whatever floats your boat. The sooner you stop expecting life to hand you the things you think you “deserve”, the easier it will be to bounce back in those times when life knocks you down and just keeps kicking. You could earn your way to that promotion and still have it snatched from under you – get over it. That doesn’t mean sit back and take it. It means that you need to learn from those ugly situations and better position yourself to reap the benefits of your diligence.

RULE 2: The World Doesn’t care about your self esteem

“The world expects you to accomplish something BEFORE you feel good about yourself” … so get on it. Start making something of yourself today. Right now. That idea you’ve been pushing to the back of your mind might be just the thing to propel you to the limelight, so get on it. You only truly fail if you never try.

RULE 3: You will not make six figure salary right after school

“You won’t be a vice president with a car phone until you earn both.” This addresses the entitled behavior that young people display on a daily basis. It’s not a good look for anyone to act like the world should unfurl a red carpet at their feet just because they showed up. You have to work hard for what you get – I’m talking sweat and blood here, and don’t expect anyone’s gratitude for it.

RULE 4: If you think your teacher is tough, wait till you meet your boss

All those deadlines you think are unreasonable at best, those times she locked you out of class because you showed up late? Ten times worse with a boss. Only, instead of chewing you out infront of a classroom, it’s a whole office. Teachers are legally mandated to show some restraint, bosses aren’t. He’ll call you all sorts of names your teacher only dreams of saying to your face, then show you the way to the unemployment line. This isn’t to scare you off of gainful employment, just to encourage you to practice dealing with difficult authority figures, to better prepare you for the future, so you can avoid an emotional outburst at the office.

RULE 5: Flipping buggers is not beneath your dignity

“Your grandparents had a different word for burger flipping – they called it OPPORTUNITY.” So get over yourself and take that job that you think is beneath you. A waiter/waitress position opens up at your favorite restaurant? Swallow your pride and take it. Use that as a stepping stone. The richest men in the world started off as paper boys – remember that.

RULE 6: If you mess up, it’s not your parents fault

“So don’t whine about your mistake – learn from them.” Too many people fall into the trap of claiming “mommy” and “daddy” issues when they mess up. According to Bill Gates, you need to stop spreading the blame around and take responsibility for your failures. Only then do you earn the right to own your successes as well.

RULE 7: Your folks know something you don’t know     

“Before you were born, your parents weren’t as boring as they are now. They got that way from paying your bills, cleaning your clothes and listening to you talk about how cool you think you are. So before you save the rainforests from the parasites of your parents’ generation, try delousing the closet in your own room.” Self-explanatory.

RULE 8: Your school may have done away with winners and losers, but life has not

“In some schools, they have abolished failing grades and they’ll give you as many tries as you want to get the right answer. This doesn’t bear the slightest resemblance to ANYTHING in real life.” Only the strong survive. This doesn’t mean that it’s okay to do whatever it takes to come out ahead – the end does not justify the means. It just means that you need to keep the big picture in mind – to remember that while it’s okay to do your best, it’s better to always ensure that you go the extra mile to prove yourself.

RULE 9: Life is not divided into semesters

“You don’t get summer off and very few employers are interested in helping you “find yourself”. Do that on your own time.” This is one thing most students don’t realize. The real world won’t give you time off to recoup your strength. Once life starts it just goes on and on, and on. The sooner you shift your way of thinking from seeing your holiday as time off, but as time to be spent making something worthwhile of yourself, the easier the transition to real life will be.

RULE 10: Television is not real life

“In real life, people actually have to leave the coffee shop and go to jobs.” This applies to more than just an episode of friends. Life seems a lot easier on TV than it is in real life. Sounds like common sense, but you’d be surprised at how many people expect to sleep in every morning and still be able to afford that dream holiday.

RULE 11: Be Nice to Nerds

“Chances are you’ll end up working for one.” Laugh all you want, but he showed that this is a very real possibility. That book worm you keep picking on for choosing the library over a twerk session could end up being your boss in the future, so be nice.

I like those statements.

2 Comments

  • Tim Rodman said

    These are great quotes. I'm very concerned for my generation, especially in the United States. We are a bunch of spoiled kids. I hope we won't need another Great Depression or World War to learn these lessons.

  • docotor said

    I don't know much about USA, but I can say that in Ukraine I also see enough spoiled children. And sadly not only children

 

Add a Comment
 

Rename Created Grid Column In Acumatica For Specific Graph Only

 

Hello everybody,

today I want to share trick about renaming item in grid. Suppose you want to use developed by Acumatica data access class of acumatica, which has name BAccount, and you have huge desire to rename it in column from "Account name" to "Employee Name".

You can use for such purpose CacheAttached event. In my case following code helped to achieve renaming:

        [PXDBString(60, IsUnicode = true)]
        [PXDefault()]
        [PXUIField(DisplayName = "Employee Name", Visibility = PXUIVisibility.SelectorVisible)]
        [PXFieldDescription]
        [PXMassMergableField]
        protected virtual void BAccount_AcctName_CacheAttached(PXCache sender)
        {
        }

No Comments

Add a Comment
 

 

Mql4 Round Numbers Indicator

 

Hello everybody,

Today I want to share with you another area. I had small order for MQL4 indicator for round numbers for Metatrader4 or as it is named MT4. The task was to draw lines at screen with some step at any currency pair. In order to implement it I started first of all Googling. Then I found almost implemented indicator at forexfactory just without option for modification steps. After wondering about code purpose of variables I created the following code:

//**************************************************
//*  RoundNr.mq4 (No Copyright)                    *
//*                                                *
//*  Draws horizontal lines at round price levels  *
//*                                                *
//*  Written by: Totoro @ forexfactory.com         *
//*  Modified by Yuriy Zaletskyy                   *
//**************************************************
#property indicator_chart_window
extern double LineSpace     = 1; // 1 unit = 0.01 of basic value (e.g. 1 USD cent)
extern color  LineColor     = Turquoise;
extern int    LineStyle     = 2;
extern string LineStyleInfo = "0=Solid,1=Dash,2=Dot,3=DashDot,4=DashDotDot";
extern string LineText      = "RoundNr ";
extern double stepSize = 0.1;
extern string stepSizeComment = "step size for jpy = 0.1, for others 0.001";
extern int digitsAfterComma = 2;
extern string digitsAfterCommaComment = "2 for jpy and 3 for non jpy pair";

double LineSpaceOld;
double HighScreen;
double Tief;
bool   FirstRun = true;

int deinit()
{
   double AbSpace = stepSize*LineSpace;
   double HigherScreen    = MathRound(110*HighScreen)/100;
   double LowerScreen   = MathRound(80*Tief)/100;
   for(double i=0; i<=HigherScreen; i+=AbSpace)
   {
      if(i<LowerScreen) { continue; }
      ObjectDelete(LineText+DoubleToStr(i,digitsAfterComma));
   }
   return(0);
}

int start()
{
   if(FirstRun)
   {
      HighScreen = NormalizeDouble( High[iHighest(NULL,0,MODE_HIGH,Bars-1,0)], digitsAfterComma );
      Tief = NormalizeDouble( Low[iLowest(NULL,0,MODE_LOW,Bars-1,0)], digitsAfterComma );
      FirstRun = false;
   }
   else if(LineSpace != LineSpaceOld)
   {
      deinit();
      HighScreen = NormalizeDouble( High[iHighest(NULL,0,MODE_HIGH,Bars-1,0)], digitsAfterComma );
      Tief = NormalizeDouble( Low[iLowest(NULL,0,MODE_LOW,Bars-1,0)], digitsAfterComma );
   }
   DrawLines();
   LineSpaceOld = LineSpace;
   return(0);
}

void DrawLines()
{
   double AbSpace = stepSize*LineSpace;
   double HigherScreen    = MathRound(110*HighScreen)/100;
   double LowerScreen   = MathRound(80*Tief)/100;

   for(double i=0; i<=HigherScreen; i+=AbSpace)
   {
      if(i<LowerScreen) { continue; }
      string StringNr = DoubleToStr(i,digitsAfterComma); // digits number in object name
if (ObjectFind(LineText+StringNr) != 0) // HLine not in main chartwindow { ObjectCreate(LineText+StringNr, OBJ_HLINE, 0, 0, i); ObjectSet(LineText+StringNr, OBJPROP_STYLE, LineStyle); ObjectSet(LineText+StringNr, OBJPROP_COLOR, LineColor); } else // Adjustments { ObjectSet(LineText+StringNr, OBJPROP_PRICE1, i); ObjectSet(LineText+StringNr, OBJPROP_STYLE, LineStyle); ObjectSet(LineText+StringNr, OBJPROP_COLOR, LineColor); } } WindowRedraw(); }

Enjoy.

No Comments

Add a Comment
 

 

Sql Formatting Tool

 

Few days ago I faced following challenge. SQL profiler gave me ugly SQL, which was unreadable for my eyes.

For example like this:

exec sp_executesql N'SELECT APAddress.AddressID, APAddress.VendorID, APAddress.VendorAddressID, APAddress.IsDefaultAddress, APAddress.RevisionID, APAddress.AddressLine1, APAddress.AddressLine2, APAddress.AddressLine3, APAddress.City, APAddress.CountryID, APAddress.State, APAddress.PostalCode, APAddress.IsValidated, APAddress.tstamp, APAddress.CreatedByID, APAddress.CreatedByScreenID, APAddress.CreatedDateTime, APAddress.LastModifiedByID, APAddress.LastModifiedByScreenID, APAddress.LastModifiedDateTime FROM APAddress APAddress WHERE (APAddress.CompanyID = 2) AND  ( APAddress.AddressID = @P0) ORDER BY APAddress.AddressID /* admin@AP.30.40.00 */',N'@P0 int',@P0=-2147483647

If you can read it, congratulations, you are genius and I'm envy of you so you can stop reading further. But if you are just programmer as me, then the next step is the following:

1. remove exec sp_executesql 

2. In some editor find and replace @P0 with value -2147483647

3. remove tail /* admin@AP.30.40.00 */',N'@P0 int',@P0=-2147483647

4. Navigate in your browser to http://www.dpriver.com/pp/sqlformat.htm

5. Copy/Paste in window and choose format mssql

6. Press format SQL.

Compare previous sql with the following:

SELECT apaddress.addressid,
       apaddress.vendorid,
       apaddress.vendoraddressid,
       apaddress.isdefaultaddress,
       apaddress.revisionid,
       apaddress.addressline1,
       apaddress.addressline2,
       apaddress.addressline3,
       apaddress.city,
       apaddress.countryid,
       apaddress.state,
       apaddress.postalcode,
       apaddress.isvalidated,
       apaddress.tstamp,
       apaddress.createdbyid,
       apaddress.createdbyscreenid,
       apaddress.createddatetime,
       apaddress.lastmodifiedbyid,
       apaddress.lastmodifiedbyscreenid,
       apaddress.lastmodifieddatetime
FROM   apaddress APAddress
WHERE  ( apaddress.companyid = 2 )
       AND ( apaddress.addressid = -2147483647 )
ORDER  BY apaddress.addressid 

IMHO the second option is easier to read.

4 Comments

  • Tim Rodman said

    Nice tip. I didn't know about that website.

    I use the SQL Prompt plug-in from Red Gate which installs as a menu in SQL Management Studio. You can also format SQL using this tool.

  • docotor said

    Nice tool. IMHO even better then web site. But according to http://www.red-gate.com/products/sql-development/sql-prompt/

    "SQL Prompt starts at $369 per user" For now I'm not ready to pay to redgate such amount

  • Tim Rodman said

    I agree, it's a little pricey, especially if you can't get your company to pay for it.

    It does save time when writing code because it auto-completes columns, tables, even table joins for you. But, if you're just looking for a formatting tool, the website works great.

  • anonymous said

    Freeware:
    Notepad++ has an add-in called "Poor Man's Sql Formatter"

    http://www.architectshack.com/PoorMansTSqlFormatter.ashx

Add a Comment
 

 

Acumatica Customization Without Acumatica Gui

 

Hello.

Today I want to share some information about Acumatica customization.

Suppose, you want to change your pages not via Acumatica UI, but via Visual Studio. Reasons why you can have such desire can be the following:

1. Visual Studio is faster then Acumatica UI

2. You can put your changes under source control

3. You can easily exchange changes with your co-workers

Let's say you have following situation.

1. You need to make changes to pages ar301000.aspx and  pm301000.aspx and pm304000.aspx.

2. You need to add new page AP508000.aspx.

3. You want to include dll, which is named DS.dll.

In order to do this you'll need the following.

1. Create in your Acumatica instance folder CstPublished

2. Inside created folder create folders pages_ar, pages_pm

3. Copy/Paste into folder pages_ar page ar301000.aspx and into pages_pm pages  pm301000.aspx and pm304000.aspx.

4. Create new page AP508000.aspx in folder AP

5. Build your dll file

6. Create following file: project.xml, with the following records:

<Customization level="0" description="">
    <File AppRelativePath="CstPublished\pages_ar\ar301000.aspx" FileID="0849bd3b-c4ce-473e-8c82-c567112aa96b" />
    <File AppRelativePath="CstPublished\pages_ar\ar301000.aspx.cs" FileID="302c4089-6046-4a17-8411-1023315f5535" />
    <File AppRelativePath="Pages\AP\AP508000.aspx" FileID="b9e88968-7f36-4cfc-9383-caed8363c269" />
    <File AppRelativePath="Pages\AP\AP508000.aspx.cs" FileID="a1ce1918-f569-4ced-8a03-b5571dc26abb" />
    <File AppRelativePath="CstPublished\pages_pm\pm301000.aspx" FileID="8c174bf0-3143-4a86-83e9-c82704c9297a" />
    <File AppRelativePath="CstPublished\pages_pm\pm301000.aspx.cs" FileID="c435350a-89dc-455c-a9cb-6356987b2889" />
    <File AppRelativePath="CstPublished\pages_pm\pm304000.aspx" FileID="65b4f056-859f-463c-99cc-32a9cde11c7d" />
    <File AppRelativePath="CstPublished\pages_pm\pm304000.aspx.cs" FileID="46b0d39b-ba12-486a-be7f-6058a37cdca1" />
</Customization>

7. Create folder with the following structure:

   7.1 Folder CstPublished which has inside of it folders pages_ar and pages_pm. And pages_ar has all ar files, and pages_pm has changes related to pm pages

   7.2 Folder Pages, which has inside of it folder AP, and AP inside of it has AP508000.aspx and AP508000.aspx.cs files

   7.3 Put into folder file project.xml

8. Zip it and your deployment is ready.

Some pitfalls which I faced.

1. xml file should be named project.xml

2. FileId is guid, and each FileID should be unique.

3. All file and folder names shouldn't be capital. Not Pages_pm but pages_pm. And not CstPublished\pages_pm\PM304000.aspx but CstPublished\pages_pm\pm304000.aspx 

4. If you find other surprises with customization let me know.

Especially for Tony I made a screenshot of my customization folder.

11 Comments

  • mazia said

    Hi
    Do you provide development support to partners?

  • docotor said

    Hello Mazia,
    for free to some degree yes. And for money definitely yes :)

  • Tony said

    Hi, I got some questions:

    1) Is there something else to do after adding the file project.xml (I zip it as you said in the /CstPublished folder with the name project.zip)? is this correct or it should go in a sub folder?

    2) The .dll also goes in /CstPublished or in a especific subfolder?

    3) I got embed SQL querys in the project.xml, It will be executed doing this? o I'll have to run in appart.

    The thing is that I added both files "project.xml" and the zipped "project.zip" and in the menu is not showing the pages menu (sitemap), I wonder if it's needed some kind of refresh to get the changes.

    I'm working with Acumatica version 4.20.0935

    Thanks for your help,
    Regards.

  • docotor said

    Hello Tony,
    Inside the folder for customization following structure should be:
    Three folders: a) "Bin", b) "CstPublished", c) "Pages" and file project.xml.
    1. If you putted file project.xml into CstPublished folder, then move it one folder higher in the hierarchy.
    2. The .dll file goes to folder bin.
    3. It's possible to embed sql into customization, but I personally use two different files ( I mean customization file and project.sql which has all db changes )

  • docotor said

    I attached screenshot of my customization. Let me know if you'll need something else

  • Tony said

    Hi again, and thanks for your reply It was very helpful. I had issues inserting the dll but I didnt notice that in the project.xml example was missing (I thought It was there xD).

    I have another question, those GUID for the FileID how are obtained? I'm working with powershell and I can't see a way to get those GUID corresponding for each file.

    Thanks...

  • docotor said

    It can be any new guid, which is not mentioned in project. I used http://createguid.com web site. In case of powershell I can propose either to read from mentioned web site, or as another option to make some simple program which will generate for you guids

  • Fernando said

    Hi Docotor!

    I have a question somehow related to this thread.
    Is it possible to include a ZIP file in the Projet.xml and then extract its content? Let me explain further:

    <File AppRelativePath="(...)\files.ZIP" FileID="0849bd3b-c4ce-473e-8c82-c567112aa96b" />

    We are facing difficulties in terms of time and path length while publishing our customization projects, so we thought that if we could just copy the ZIP file and then execute a command that would extract its content, then we could solve both problems at once. Is this feasible?

    Thanks for your help :)

  • docotor said

    I don't think that it's possible. Actually I don't know. I just can propose you workaround. In your destination Acumatica instance locate folder CstPublished, and inside of it put your modified pages. Then you'll not need to include modified aspx pages into zip file. Just keep in mind naming conventions. In CstPublished should be ar301000 not AR301000. As far as I know Acumatica engine will make analysis of this folder and will consider them as active. If to speak about your pages which you made completely by yourself you can put them into Acumatica directory straight away. I also assume that you can put in Bin folder compiled version of your dll.

  • Ronan Masangcay said

    I need to code on my local machine using Visual Studio (VS) VS2013 but will deploy the DLL on another machine that has an Acumatica ERP instance. Can I code in VS2013 in my PC and see the Acumatica objects (particularly, Data Providers) in my VS2013; my PC has Acumatica Framework already.

    Please let me know how

    TY

  • docotor said

    You can code locally on your VS2013 and then you'll need to put dll and pages into Acumatica customization and then publish it at your Acumatica instance.

 

Add a Comment
 

 

Get All Columns Of Specific Table

 

Today I needed to get all columns of specifict table. 

SQL which you can use for this purpose is the following:

SELECT COLUMN_NAME 'All_Columns' FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='Specific table'

No Comments

 

Add a Comment
 

 

Screen Numbering In Acumatica

 

Hello everybody.

Today I want in short share screen numering in Acumatica.

Let's say we have screen number like this: xx.yy.zz.tt

in that case

yy is screen sequential number

zz is screen type: 10 - setup, 20 - Maintenance, 30 - data entry, 40 - inquiry, 50 - processing, 60 - reports.

tt is sub screen sequential number

1 Comment

  • maillot france 2014 said

    I am really thankful to the owner of this site who has shared this wonderful post at at this place.

Add a Comment
 

 

Acumatica Unit Test

 

Hello readers of mine blog.

Today I want to share with everybody who wants to make unit test of Acumatica how I achieved it.

For unit testing I use NUnit. In order to start my work with Acumatica Unit testing I wrote the following class:

    [TestFixture]
    public class CATranEntryExtTest
    {
        [Test]
        public void TestAutonumber()
        {
            var gr = PXGraph.CreateInstance<CATranEntry>();
        }
    }

Now with help of Resharper let's start debugging:

And here we go, the first error message:

PX.Data.PXProviderException : Provider cannot be instantiated.

   at PX.Data.PXDatabase.get_Provider() in c:\Builders\4_10-2013_12_16-23_17_15-Full\Scripts\BuildTemp\NetTools\PX.Data\Database\Provider.cs: line 388

   at PX.Data.PXDatabase.GetSlot(String key, Type[] tables) in c:\Builders\4_10-2013_12_16-23_17_15-Full\Scripts\BuildTemp\NetTools\PX.Data\Database\Provider.cs: line 802

   at PX.Data.PXGraph.a(Type A_0) in c:\Builders\4_10-2013_12_16-23_17_15-Full\Scripts\BuildTemp\NetTools\PX.Data\Graph\Graph.cs: line 2684

   at PX.Data.PXGraph.CreateInstance(Type graphType) in c:\Builders\4_10-2013_12_16-23_17_15-Full\Scripts\BuildTemp\NetTools\PX.Data\Graph\Graph.cs: line 112

   at PX.Data.PXGraph.CreateInstance() in c:\Builders\4_10-2013_12_16-23_17_15-Full\Scripts\BuildTemp\NetTools\PX.Data\Graph\Graph.cs: line 57

So, it means we need to add some connection string to the site.  How to add it exactly? What proper way? I tried two next options:

1.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <remove name="ProjectX" />
    <add name="ProjectX" providerName="System.Data.SqlClient" connectionString="Data Source=DIO222\DIO195;Initial Catalog=Ac20140117;Integrated Security=False;User ID=sa;Password=1234" />
  </connectionStrings>
</configuration>



This way also:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionString value="Data Source=DIO222\DIO195;Initial Catalog=Ac20140117;Integrated Security=False;User ID=sa;Password=1234"></connectionString>
</configuration>

and if you suppose, that error message changed, you are wrong. It didn't go away.

Then I addressed support of Acumatica, and here is the answer:

 you just need this config. That is not necessary to create and use real database. You just need connectionString specified.

In case if with answer you know how to add connectionString to app.config for unit test and it will work, let me congratulate you. For me you are genious. Sadly to admit, I'm not. After a while, I decided to do crazy idea - copy/paste web.config into App.config. Imagine scale of my surprise when I noticed, that error went away!!!!!!

Now I got the next error message:

PX.Data.PXNotLoggedInException : You are not currently logged in.

Do you have any idea how to log in? 

In order to understand this idea lets go to Login.aspx.cs, maybe we can locate there something. For example method NormalLogin:

    private void NormalLogin(string[] companies)
    {
        if (companies != null && companies.Length == 1)
        {
            cmbCompany.Items.Clear();
            cmbCompany.Items.Add(companies[0]);
        }

        string loginText = txtUser.Text.Trim();
        string userName = PXDatabase.Companies.Length > 0 ? loginText + "@" +
            (cmbCompany.SelectedIndex != -1 ? cmbCompany.SelectedItem.Value : PXDatabase.Companies[0]) : loginText;

        if (!PXLogin.LoginUser(ref userName, txtPass.Text))
        {
            // we will change password during next round-trip
            PXContext.Session.SetString("ChangingPassword", txtPass.Text);

            DisablingUserPassword();
            EnablingChangingPassword();

            this.Master.Message = string.Empty;
        }
        else
        {
            PXLogin.InitUserEnvironment(userName, cmbLang.SelectedValue);
        }
    }

I'm considered about mehtod PXLogin.LoginUser. Lets use reflector in order to look there:

 

using (PXLoginScope pxLoginScope = new PXLoginScope(userName, new string[0]))using (PXLoginScope pxLoginScope = new PXLoginScope(userName, new string[0]))

I just think what can mean PXLoginScope. But maybe name is self-explanatory. Some scope where some login considered as working?

Lets write code similar to what reflector showed:

[Test]
public void TestAutonumber()
{
     using (var pxLoginScope = new PXLoginScope("someuserName", new string[0]))
     {
          var gr = PXGraph.CreateInstance<CATranEntry>();
     }
}

and now error message is 

PX.Data.PXUndefinedCompanyException : Unable determine proper company id for the request.

At least we can see, that error message changes during our thinking process :). 

All other conclusions were built on this code:

        MembershipUser membershipUser = (MembershipUser) null;
        try
        {
          if (Membership.ValidateUser(pxLoginScope.UserName, password))
          {
            membershipUser = Membership.GetUser(pxLoginScope.UserName);
            if (membershipUser != null)
            {
              pxLoginScope.UserName = membershipUser.UserName;
              userName = PXContext.PXIdentity.User.Identity.Name;
              if (pxLoginScope.CompanyName != null)
              {
                string[] rolesForUser = System.Web.Security.Roles.GetRolesForUser(pxLoginScope.UserName);
                if (rolesForUser == null || rolesForUser.Length == 0)
                  throw new PXException("You are not allowed to login to the company {0}.", new object[1]
                  {
                    (object) pxLoginScope.CompanyName
                  });
                else if (!Enumerable.Contains<string>((IEnumerable<string>) PXDatabase.AvailableCompanies, pxLoginScope.CompanyName))
                  throw new PXException("You are not allowed to login to the company {0}.", new object[1]
                  {
                    (object) pxLoginScope.CompanyName
                  });
              }
              if (PXLogin.c(pxLoginScope.UserName))
                return false;
              PXLogin.a(pxLoginScope.UserName, false);
              PXLogin.SetBranchID(pxLoginScope.UserName, pxLoginScope.CompanyName, pxLoginScope.Branch);
              PXSessionContextFactory.AuthenticateRequest();
              PXLicenseHelper.OnAuthenticate();
              return true;
            }

The function which has name a if to trust to reflector just updates some info in audit journal, and updates some info in db, which IMHO is not usable for unit test.

So, code, which I use now for initiating Unit test now looks like this:

    [TestFixture]
    public class CATranEntryExtTest
    {
        [Test]
        public void TestAutonumber()
        {
            string userName = "some user name in db";
            using (var pxLoginScope = new PXLoginScope(userName, new string[0]))
            {
                var membershipUser = (MembershipUser) null;
                // Membership.ValidateUser(pxLoginScope.UserName, "123");

                membershipUser = Membership.GetUser(pxLoginScope.UserName);
                if (membershipUser != null)
                {
                    pxLoginScope.UserName = membershipUser.UserName;
                    userName = PXContext.PXIdentity.User.Identity.Name;
                    if (pxLoginScope.CompanyName != null)
                    {
                        string[] rolesForUser = System.Web.Security.Roles.GetRolesForUser(pxLoginScope.UserName);
                        if (rolesForUser == null || rolesForUser.Length == 0)
                            throw new PXException("You are not allowed to login to the company {0}.", new object[1]
                                {
                                    (object) pxLoginScope.CompanyName
                                });
                        else if (
                            !Enumerable.Contains<string>((IEnumerable<string>) PXDatabase.AvailableCompanies,
                                                         pxLoginScope.CompanyName))
                            throw new PXException("You are not allowed to login to the company {0}.", new object[1]
                                {
                                    (object) pxLoginScope.CompanyName
                                });
                    }
                    PXLogin.SetBranchID(pxLoginScope.UserName, pxLoginScope.CompanyName, pxLoginScope.Branch);
                    var gr = PXGraph.CreateInstance<CATranEntry>();
                }
            }
          }
    }

At the end of execution gr looks like this:

Lets continue our way of unit testing for creating instance of our extension. This can be achieved via following way:

var accountsManager = gr.GetExtension<CATranEntryExt>();

Screenshot shows that we created instance of extension successfully:

Now I can test methods, which are inside of the extension CATranEntryExt.

I think it's enough for this article. If you have any comments, requests, wishes, critics, you are wormly welcome to express them.

 

Acumatica Unit Test

Acumatica unit test

Hello readers of mine blog.

Today I want to share with everybody who wants to make unit test of Acumatica how I achieved it.

For unit testing I use NUnit. In order to start my work with Acumatica Unit testing I wrote the following class:

    [TestFixture]
    public class CATranEntryExtTest
    {
        [Test]
        public void TestAutonumber()
        {
            var gr = PXGraph.CreateInstance<CATranEntry>();
        }
    }

Now with help of Resharper let's start debugging:

And here we go, the first error message:

PX.Data.PXProviderException : Provider cannot be instantiated.

   at PX.Data.PXDatabase.get_Provider() in c:\Builders\4_10-2013_12_16-23_17_15-Full\Scripts\BuildTemp\NetTools\PX.Data\Database\Provider.cs: line 388

   at PX.Data.PXDatabase.GetSlot(String key, Type[] tables) in c:\Builders\4_10-2013_12_16-23_17_15-Full\Scripts\BuildTemp\NetTools\PX.Data\Database\Provider.cs: line 802

   at PX.Data.PXGraph.a(Type A_0) in c:\Builders\4_10-2013_12_16-23_17_15-Full\Scripts\BuildTemp\NetTools\PX.Data\Graph\Graph.cs: line 2684

   at PX.Data.PXGraph.CreateInstance(Type graphType) in c:\Builders\4_10-2013_12_16-23_17_15-Full\Scripts\BuildTemp\NetTools\PX.Data\Graph\Graph.cs: line 112

   at PX.Data.PXGraph.CreateInstance() in c:\Builders\4_10-2013_12_16-23_17_15-Full\Scripts\BuildTemp\NetTools\PX.Data\Graph\Graph.cs: line 57

So, it means we need to add some connection string to the site.  How to add it exactly? What proper way? I tried two next options:

1.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <remove name="ProjectX" />
    <add name="ProjectX" providerName="System.Data.SqlClient" connectionString="Data Source=DIO222\DIO195;Initial Catalog=Ac20140117;Integrated Security=False;User ID=sa;Password=1234" />
  </connectionStrings>
</configuration>



This way also:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionString value="Data Source=DIO222\DIO195;Initial Catalog=Ac20140117;Integrated Security=False;User ID=sa;Password=1234"></connectionString>
</configuration>

and if you suppose, that error message changed, you are wrong. It didn't go away.

Then I addressed support of Acumatica, and here is the answer:

 you just need this config. That is not necessary to create and use real database. You just need connectionString specified.

In case if with answer you know how to add connectionString to app.config for unit test and it will work, let me congratulate you. For me you are genious. Sadly to admit, I'm not. After a while, I decided to do crazy idea - copy/paste web.config into App.config. Imagine scale of my surprise when I noticed, that error went away!!!!!!

Now I got the next error message:

PX.Data.PXNotLoggedInException : You are not currently logged in.

Do you have any idea how to log in? 

In order to understand this idea lets go to Login.aspx.cs, maybe we can locate there something. For example method NormalLogin:

    private void NormalLogin(string[] companies)
    {
        if (companies != null && companies.Length == 1)
        {
            cmbCompany.Items.Clear();
            cmbCompany.Items.Add(companies[0]);
        }

        string loginText = txtUser.Text.Trim();
        string userName = PXDatabase.Companies.Length > 0 ? loginText + "@" +
            (cmbCompany.SelectedIndex != -1 ? cmbCompany.SelectedItem.Value : PXDatabase.Companies[0]) : loginText;

        if (!PXLogin.LoginUser(ref userName, txtPass.Text))
        {
            // we will change password during next round-trip
            PXContext.Session.SetString("ChangingPassword", txtPass.Text);

            DisablingUserPassword();
            EnablingChangingPassword();

            this.Master.Message = string.Empty;
        }
        else
        {
            PXLogin.InitUserEnvironment(userName, cmbLang.SelectedValue);
        }
    }

I'm considered about mehtod PXLogin.LoginUser. Lets use reflector in order to look there:

using (PXLoginScope pxLoginScope = new PXLoginScope(userName, new string[0]))using (PXLoginScope pxLoginScope = new PXLoginScope(userName, new string[0]))

I just think what can mean PXLoginScope. But maybe name is self-explanatory. Some scope where some login considered as working?

Lets write code similar to what reflector showed:

[Test]
public void TestAutonumber()
{
     using (var pxLoginScope = new PXLoginScope("someuserName", new string[0]))
     {
          var gr = PXGraph.CreateInstance<CATranEntry>();
     }
}

and now error message is 

PX.Data.PXUndefinedCompanyException : Unable determine proper company id for the request.

At least we can see, that error message changes during our thinking process :). 

All other conclusions were built on this code:

        MembershipUser membershipUser = (MembershipUser) null;
        try
        {
          if (Membership.ValidateUser(pxLoginScope.UserName, password))
          {
            membershipUser = Membership.GetUser(pxLoginScope.UserName);
            if (membershipUser != null)
            {
              pxLoginScope.UserName = membershipUser.UserName;
              userName = PXContext.PXIdentity.User.Identity.Name;
              if (pxLoginScope.CompanyName != null)
              {
                string[] rolesForUser = System.Web.Security.Roles.GetRolesForUser(pxLoginScope.UserName);
                if (rolesForUser == null || rolesForUser.Length == 0)
                  throw new PXException("You are not allowed to login to the company {0}.", new object[1]
                  {
                    (object) pxLoginScope.CompanyName
                  });
                else if (!Enumerable.Contains<string>((IEnumerable<string>) PXDatabase.AvailableCompanies, pxLoginScope.CompanyName))
                  throw new PXException("You are not allowed to login to the company {0}.", new object[1]
                  {
                    (object) pxLoginScope.CompanyName
                  });
              }
              if (PXLogin.c(pxLoginScope.UserName))
                return false;
              PXLogin.a(pxLoginScope.UserName, false);
              PXLogin.SetBranchID(pxLoginScope.UserName, pxLoginScope.CompanyName, pxLoginScope.Branch);
              PXSessionContextFactory.AuthenticateRequest();
              PXLicenseHelper.OnAuthenticate();
              return true;
            }

The function which has name a if to trust to reflector just updates some info in audit journal, and updates some info in db, which IMHO is not usable for unit test.

So, code, which I use now for initiating Unit test now looks like this:

    [TestFixture]
    public class CATranEntryExtTest
    {
        [Test]
        public void TestAutonumber()
        {
            string userName = "some user name in db";
            using (var pxLoginScope = new PXLoginScope(userName, new string[0]))
            {
                var membershipUser = (MembershipUser) null;
                // Membership.ValidateUser(pxLoginScope.UserName, "123");

                membershipUser = Membership.GetUser(pxLoginScope.UserName);
                if (membershipUser != null)
                {
                    pxLoginScope.UserName = membershipUser.UserName;
                    userName = PXContext.PXIdentity.User.Identity.Name;
                    if (pxLoginScope.CompanyName != null)
                    {
                        string[] rolesForUser = System.Web.Security.Roles.GetRolesForUser(pxLoginScope.UserName);
                        if (rolesForUser == null || rolesForUser.Length == 0)
                            throw new PXException("You are not allowed to login to the company {0}.", new object[1]
                                {
                                    (object) pxLoginScope.CompanyName
                                });
                        else if (
                            !Enumerable.Contains<string>((IEnumerable<string>) PXDatabase.AvailableCompanies,
                                                         pxLoginScope.CompanyName))
                            throw new PXException("You are not allowed to login to the company {0}.", new object[1]
                                {
                                    (object) pxLoginScope.CompanyName
                                });
                    }
                    PXLogin.SetBranchID(pxLoginScope.UserName, pxLoginScope.CompanyName, pxLoginScope.Branch);
                    var gr = PXGraph.CreateInstance<CATranEntry>();
                }
            }
          }
    }

At the end of execution gr looks like this:

Lets continue our way of unit testing for creating instance of our extension. This can be achieved via following way:

var accountsManager = gr.GetExtension<CATranEntryExt>();

Screenshot shows that we created instance of extension successfully:

Now I can test methods, which are inside of the extension CATranEntryExt.

I think it's enough for this article. If you have any comments, requests, wishes, critics, you are wormly welcome to express them.

14 Comments

  • Steven said

    Acumatica is horrible. It's just bad.

  • docotor said

    Partially agree with you, it has a lot of surprises :). But from my experience with ERP, all of big ERPs had surprises. Starting from Micrsofot Dynamics, SAP. All of them require to be good developer, which can critically apply what he finds in documentation

  • Tim Rodman said

    ERP products definitely aren't "one size fits all". Acumatica isn't for everyone, but I think it can be a good fit for a lot of companies.

    Of course, most of the headaches associated with ERP implementations aren't even related to the specific ERP product chosen:
    http://panorama-consulting.com/who-needs-help-managing-an-erp-implementation/

    So I'm sure that Acumatica become the scapegoat for some failed ERP implementations, but it's the same with every ERP product.

  • docotor said

    Tim, I can't say better then you. I just want to add that depending from task or set of tasks company or person can need totally different product. For somebody will be enough nopcommerce, for somebody just simple web site.

  • Ray Ban 5226 Kolor 2034 said

    Please dont think Im just trying to copy you, but I really like the formatof this site. Could you let me know which theme are you using? Or was it custom made?

  • docotor said

    Hi Ray Ban 5226 Kolor 2034,
    I'm using Orchard, Classig Blog Them

  • Buy Ray Ban Glasses Online In Pakistan said

    Please, can you Mail me and tell me few more thinks about this, I am really fan of your bloggets solved properly asap.

  • anonymous said

    I really like your writing style, excellent info

  • chaussures said

    This actually answered my problem, thanks!

  • Tony said

    Hi again, great article about starting a unit testing in Acumatica, I tried to apply it to my test project but it fails =(
    it says "Test 'M:---.---.UnitTests.MyFirstTestClass.TestAutonumber' failed:
    The type initializer for 'PX.Data.PXCache`1' threw an exception."
    Not sure what I'm missing here (I set an valid user and uncommented the line "Membership.ValidateUser..." and added the password of that user). Thanks in advance.

    -Regards.

  • docotor said

    Hi Tony, mentioned error message I seen, when passwords were hashed. Maybe you have the same. If you wish, I can send you source code of my test project.

  • Tony said

    Well, in my db the passwords are not hashed... =/
    In visual Studio I had to add some references because they were not set by default (maybe some reference in particular I'm missing?). I will appreciate if you send to my email (____) your source code, thanks in advance.

    Keep with the great blog and feedback... =)

  • docotor said

    Just send you with additional comments

Add a Comment