1

I would like to create a nightly snapshot of certain tables in my SAAS-hosted Acumatica instance and SFTP the resulting XML file to a given location. (I've created a custom Export Mode option for just the tables of interest.)

I would like to do this process through an Acumatica Automation Schedule, a custom Action that I can call through the API, or an API call to existing Acumatica Actions, or some combination of the above.

However, it doesn't appear that these options are available to me:

  • Automation Scheduling doesn't support snapshot creation (https://feedback.acumatica.com/ideas/ACU-I-570)
  • I tried adding the Action to create a snapshot to the web service endpoint, but it doesn't appear that I can pass the parameters I would need to manage the pop-ups
  • Attempting to create a custom Acumatica button, I'm also to struggling to figure out how to raise and manage the pop-ups.

Once I have the snapshot created, I presume I will need to be able to download it locally in order to SFTP it to my desired location; I haven't gotten far enough to know if I invoke the download snapshot button through the API where the resulting file will go.

June B
  • 141
  • 10

2 Answers2

1

June, When I get stuck with stuff that I am unable to trigger with ReST or other integration techniques I generally turn to Selenium as the path of least resistance. I do want to point out I always err on the side of using selenium as a last resort. I generally like to use the PowerShell selenium module for stuff like this. Once you have your script working you can wire it into a standard Windows Scheduler real easy. It may not be the most elegant way to do it but it will certainly get the job done. if your interested you can get started with this

https://github.com/adamdriscoll/selenium-powershell/blob/master/README.md

Once you get familiar with it you use the chrome inspection tool to dig into the elements that you need to target. the dialog boxes you are after are often found as iframes within the page.

I can share some of my scripts to help you get started if you want to try this route.

I hope this helps. Robert

Robert Waite
  • 271
  • 1
  • 7
  • 1
    June, sorry for the delay in getting this to you. This is a recent script i have been pulling together to flow through a customer portal shopping cart process. See if this points you in the right direction. https://www.dropbox.com/s/clzpr607hy9wdjk/Test-AcuPortalShoppingCart.ps1?dl=0 – Robert Waite Mar 03 '20 at 15:24
  • Also, I have volunteered to do a Session on automating things with Powershell for the next Virtual Dev Conference. If we do indeed deem that we are unable to do this with ReST or other means of automation I will be inclined to help with this further as to beable to use this as a specific use case for using PowerShell and Selenium. Im eager to see this through so keep me up to date on how you make out. – Robert Waite Mar 03 '20 at 15:28
  • I've been playing with the Selenium IDE; when I record a script the playback struggles with dropdown options -- it either doesn't recognize the dropdown element that was recorded when I clicked, or if I click into the box itself and type the appropriate option it reverts back to the first option in the list with the click into the next element. I've just barely started playing with WebDriver inside one of our service applications in the hopes that might have better luck, since we already have a framework set up to fire off a scheduled task that calls into that application. – June B Mar 03 '20 at 16:32
  • I have concerns about WebDriver being similarly unable to handle certain elements on this page, but want to keep plowing forward. I don't have experience with Powershell in general, but I would be very grateful for the opportunity to collaborate on this if it's of interest to you! – June B Mar 03 '20 at 16:32
  • I never found the Selenium IDE very useful. I just identify the elements with the inspection tools. There are some things more challenging and you often hit race conditions with selenium so you need to slow things down with using a WebDriverWait or a crude sleep delay. All in all selenium is a great addition to your tool belt. – Robert Waite Mar 05 '20 at 00:06
0

I ended up creating a simple console application since it was more in line with our other applications and I have more familiarity with C# than with PowerShell. Robert, your project was invaluable to figuring out how to reference the trickier elements.

I expect to set up scheduled tasks that will call my application with the method name of each step, with appropriate delays between each -- creating the snapshot takes about 25 minutes, for example. There are separate methods to create the snapshot, download the snapshot, delete the snapshot, and next I'm working on SFTPing the downloaded snapshot to its end destination. I put in sleeps to allow time for the website to catch up; there are Waits and WaitForExpectedCondition methods available but I didn't get into them in this quick and dirty version.

Here's the guts of my code. (I added WebDriver and ChromeDriver to the application via Nuget.)

using System;
using System.Collections.Generic;
using System.Linq;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using System.Threading;

namespace InfiniteExport
{
   class Program
    {
        static string connectorInfo;
        static void Main(string[] args)
        {
            string method = "";
            if (args.Count() >= 1)
            {
                method = args[0].ToLower();
            }

            IWebDriver driver = new ChromeDriver(); 
            try
            {
                switch (method)
                {
                    case "createsnapshot":                        
                        Login(driver);                        
                        CreateSnapshot(driver);
                        break;
                    case "downloadsnapshot":
                        Login(driver);
                        DownloadSnapshot(driver);
                        break;
                    case "deletesnapshot":
                        Login(driver);
                        DeleteSnapshot(driver);
                        break;
                    default:
                        break;
                }
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                driver.Quit();
            }
        }

        static void Login(IWebDriver driver)
        {
            //This login actually results in a 404 error because there's no redirect embedded in it, but the login itself is successful and creates the session used by the next method navigation 
            driver.Navigate().GoToUrl(InfiniteExport.Properties.Settings.Default.BaseAcumaticaURL + "/Frames/Login.aspx");
            driver.FindElement(By.Id("form1")).Click();
            driver.FindElement(By.Id("txtUser")).SendKeys(InfiniteExport.Properties.Settings.Default.AcumaticaUserName);
            driver.FindElement(By.Id("txtPass")).SendKeys(InfiniteExport.Properties.Settings.Default.AcumaticaPassword);
            driver.FindElement(By.Id("btnLogin")).Click();
            driver.Manage().Window.Maximize();
            Thread.Sleep(5000);
        }

        static void CreateSnapshot(IWebDriver driver)
        {
            driver.Navigate().GoToUrl(@InfiniteExport.Properties.Settings.Default.BaseAcumaticaURL + "/Main?ScreenId=SM203520&_CompanyID=2");
            Thread.Sleep(2000);
            driver.SwitchTo().Frame("main");
            //Click the @$@#%*! unnamed create snapshot button
            driver.FindElement(By.CssSelector(".toolsBtn[data-cmd=exportSnapshotCommand]")).Click();
            Thread.Sleep(2000);
            //Switch to the modal popup to start clicking items on it (this is the "Warning not in maintenance mode" popup)
            driver.SwitchTo().ActiveElement();
            driver.FindElement(By.Id("messageBox_0")).Click();
            Thread.Sleep(2000);
            //Switch to the modal popup with the export options
            driver.SwitchTo().ActiveElement();
            driver.FindElement(By.Id("ctl00_phF_pnlExportSnapshot_frmExportSnapshot_edDescription")).SendKeys("InfiniteExport");

            //Select the dropdown option for the InfiniteExport
            driver.FindElement(By.Id("ctl00_phF_pnlExportSnapshot_frmExportSnapshot_edExportMode_text")).Click();
            driver.FindElement(By.Id("ctl00_phF_pnlExportSnapshot_frmExportSnapshot_edExportMode_text")).SendKeys("InfiniteExport");
            Thread.Sleep(2000);
            driver.FindElement(By.ClassName("ddSelection")).Click();

            driver.FindElement(By.Id("ctl00_phF_pnlExportSnapshot_frmExportSnapshot_chkPrepare")).Click();

            //Select the dropdown option for XML
            driver.FindElement(By.Id("ctl00_phF_pnlExportSnapshot_frmExportSnapshot_edPrepareMode")).Click();
            driver.FindElement(By.Id("ctl00_phF_pnlExportSnapshot_frmExportSnapshot_edPrepareMode_text")).SendKeys("XML");
            Thread.Sleep(2000);
            driver.FindElement(By.ClassName("ddSelection")).Click();
            Thread.Sleep(2000);

            //Click the OK button to start the export
            driver.FindElement(By.Id("ctl00_phF_pnlExportSnapshot_btnExportSnapshotOK")).Click();

            //Wait long enough for the process to start, then quit and come back later to download
            Thread.Sleep(10000);
        }

        static void DownloadSnapshot(IWebDriver driver)
        {
            driver.Navigate().GoToUrl(@InfiniteExport.Properties.Settings.Default.BaseAcumaticaURL + "/Main?ScreenId=SM203520&_CompanyID=2");
            Thread.Sleep(2000);
            driver.SwitchTo().Frame("main");
            //Unless this is made fancier, it will download the active grid row, which is the most recent snapshot created
            driver.FindElement(By.CssSelector(".toolsBtn[data-cmd=downloadSnapshotCommand]")).Click();
            Thread.Sleep(10000);
        }

        static void DeleteSnapshot(IWebDriver driver)
        {
            driver.Navigate().GoToUrl(@InfiniteExport.Properties.Settings.Default.BaseAcumaticaURL + "/Main?ScreenId=SM203520&_CompanyID=2");
            Thread.Sleep(2000);
            driver.SwitchTo().Frame("main");
            //Unless this is made fancier, it will delete the active grid row, which is the most recent snapshot created
            driver.FindElement(By.CssSelector(".toolsBtn[data-cmd=Delete]")).Click();
            Thread.Sleep(2000);
            driver.FindElement(By.CssSelector(".toolsBtn[data-cmd=saveCompanyCommand]")).Click();
            Thread.Sleep(10000);
        }
    }
}
June B
  • 141
  • 10
  • That's awesome!, I am happy to see you made progress. For something quick and dirty Ill will whip up a PowerShell script to start the process then transfer the work into a C# Integration test framework. – Robert Waite Mar 05 '20 at 00:00
  • 1
    The selenium PowerShell module is simply a wrapper around the C# WebDriver you found and used above. It's better to know it C# side as you get a deeper understanding of how it all works. If you get into PowerShell end more you will find all the methods you know C# side also work in PowerShell – Robert Waite Mar 05 '20 at 00:09