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.

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.

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.

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'

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

 

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 security

Hello,

this post is dedicated to understanding how security in Acumatica works. If to be precisive security of accounts. 

Let's say you need in some way filter accounts for some user according to configuration at row level security screen ( GL104000 ).

In my case I had following records in database which correspondent to the following numbers:

Relation group mask : 16; 0; 0; 0

User group mask: 159; 96; 0; 0

Account group mask: 16; 0; 0; 0

How to join them? They are joined not by separated table, which has relations between groups and accounts or groups and users, but with usage of bit mask. It's very effective way to join and is much faster then holding adta in db. But the most complicated from viewpoint of understanding. Lets convert already mentioned numbers into bit masks:

Relation group mask :

00010000; 00000000; 00000000; 00000000

User group mask:

10011111; 11000000; 00000000; 00000000

Account group mask:

00010000; 00000000; 00000000; 00000000

From the bit mask you can notice that there is common bit between first byte in Account group mask and User group mask. Also there is common bit between "User group mask" and Relation group mask which means that user belongs to the group, and account belongs to the group. In order to work with them I wrote the following code:

 public List<Account> GetAvailableCashAccounts()
 {
     var result = new List<Account>();
     var userID = PXAccess.GetUserID();
     var user = GetUsers().First(u => u.PKID == userID);
     var allAccounts = GetAccounts();
     var groups = GetGroups();
     foreach (var account in allAccounts)
     {
           foreach (var relationGroup in groups)
           {
             for (var i = 0; i < relationGroup.GroupMask.Length && i < account.GroupMask.Length; i++)
               {
                  if ((relationGroup.GroupMask[i] & account.GroupMask[i]) == 0) continue;
                  if ((account.GroupMask[i] & user.GroupMask[i]) == 0 || result.Find(a => a.AccountCD.Equals(account.AccountCD)) != null) continue;
                  result.Add(account);
                 }
             }
       }
       return result;
}

As usually all your comments are welcomed, considered, premoderated, banned :)

Autonumbering in Acumatica second way

In one of my previous posts I described how to add autonumbering to acumatica. Described method will work pretty well, if you don't have any pop-ups which also need to have autonumbering. In case if you have such pop-ups, then it is different story.

My next assignment on the job was to add autonumbering to the screen "Fund Transfers" which is marked as "CA301000" to the field Document ref at three places:

There is a saying, that one image is better then thousands of words :).

Here is example of my code for OutExtRefNbr:

        protected void CATransfer_OutExtRefNbr_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
        {
            var row = e.Row as CATransfer;
            if (row == null)
                return;
            var refNbr = "001224";
            e.NewValue = refNbr;
        }

 

If you have a question why on earth code below needed:

                   var row = e.Row as "Class for which we will provide extension";

                   if( row == null )

                      return;

the answer is simple: because Acumatica for some unknown reason calls method FieldDefaulting and for mysterious reason passes into e.Row null. If you have any idea why, you are welcome to share them. I don't have and just used workaround ( don't say it to my boss ).

 

The second part of my task was to add some value to pop-up window. That can be done via the following template:

public class "Class Extention name" : PXGraphExtension<"Class for which we will provide extension">

{

      protected void "Class for which we will provide extension"_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)

      {

             Base.AddFilter.Current."Field in pop-up" = "some default value";

            Base.AddFilter.Update(Base.AddFilter.Current);

       }

"Field in pop-up" you can find also in Design mode of Acumatica forms.

Here is fragment of my source code for changes:

protected void CATransfer_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
     Base.AddFilter.Current.ExtRefNbr = "0012224";
     Base.AddFilter.Update(Base.AddFilter.Current);
}

In case if you have any comments, ideas don't hesitate to share them via comments line.

There is a saying, that one image is better then thousands of words :).

Here is example of my code for OutExtRefNbr:

        protected void CATransfer_OutExtRefNbr_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
        {
            var row = e.Row as CATransfer;
            if (row == null)
                return;
            var refNbr = "001224";
            e.NewValue = refNbr;
        }

 

If you have a question why on earth code below needed:

                   var row = e.Row as "Class for which we will provide extension";

                   if( row == null )

                      return;

the answer is simple: because Acumatica for some unknown reason calls method FieldDefaulting and for mysterious reason passes into e.Row null. If you have any idea why, you are welcome to share them. I don't have and just used workaround ( don't say it to my boss ).

 

The second part of my task was to add some value to pop-up window. That can be done via the following template:

public class "Class Extention name" : PXGraphExtension<"Class for which we will provide extension">

{

protected void "Class for which we will provide extension"_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
      Base.AddFilter.Current."Field in pop-up" = "some default value";
      Base.AddFilter.Update(Base.AddFilter.Current);
}

"Field in pop-up" you can find also in Design mode of Acumatica forms.

Here is fragment of my source code for changes:

protected void CATransfer_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
     Base.AddFilter.Current.ExtRefNbr = "0012224";
     Base.AddFilter.Update(Base.AddFilter.Current);
}

In case if you have any comments, ideas don't hesitate to share them via comments line.

 

Copy entry types via web services in Acumatica from one instance into another

Hello everybody.

Today I want to share how to copy Entty Types items in acumaticca from one instance into another instance.

The first step was to create import export settings in both instances of acumatica as described in acumatica manual similar to this screenshot:

The second step was to create project, which I decided to make as windows forms application with the following input fields: