Импорт тысяч записей в Acumatica через API-интерфейс на основе контрактов SOAP - PullRequest
0 голосов
/ 01 декабря 2018

Я использую контрактные API-интерфейсы SOAP, чтобы попытаться импортировать около 25 000 строк в журнале записей из банковской системы в одну партию Acumatica GL.

  1. Если я попытаюсь добавить все записи одновременно в одну и ту же серию GL, мой запрос истечет через несколько часов.Поскольку в нем используется одна и та же партия GL, это решение не использует многопоточность.

  2. Я также попытался добавить 25000 строк по одной строке за раз в одну серию GL иколичество запросов не истекает, но скорость работы начинает значительно снижаться после добавления примерно 3000 записей в пакет GL.Этот процесс занимает несколько часов, и, поскольку он использует один и тот же пакет GL, это решение не использует многопоточность.

  3. Я также изучил многопоточность для импорта данных внесколько небольших GL-партий по 5000 строк каждая, и это работает без проблем с тайм-аутом.но это все еще занимает около полутора часов, чтобы бежать.Кроме того, клиент не принимает этот многопартийный подход;они хотят, чтобы все их ежедневные данные в одной партии GL.

25 000 записей, мне кажется, не слишком много, поэтому мне интересно, если API Acumatica не были построены для такого количества строк водна транзакция.Все, что я делаю в своем коде, - это создание информации об объекте, читая текстовый файл, а затем вызывая метод put для создания пакета GL с использованием этого объекта с 25 000 записей строк.

Я прочитал парустатьи об оптимизации API, но они в основном имеют дело с разными экземплярами сущности, как, например, в нескольких разных партиях GL или нескольких разных товарах.В этих случаях многопоточность является большим преимуществом, потому что вы можете иметь несколько потоков, создающих несколько «разных» пакетов GL, но многопоточность не помогает при обновлении одного и того же пакета GL.

Вот что я прочитал до сих пор:

https://asiablog.acumatica.com/2016/12/optimizing-large-import.html

https://adn.acumatica.com/blog/contractapioptimization/

Я в недоумении, так что любые указателиБуду очень признателен.

Я с нетерпением жду вашего ответа.

Вот мой код:

    public static void CreateMultipleLinesPerJournalEntryBatchContractTEST(MyStoreContract.DefaultSoapClient soapClient, List<JournalEntry> journalEntries)
    {
        string myModuleForBatchLookup = "GL";

        //list holding the values of all the records belonging to the batch in process
        List<JournalEntry> allBatchItems = journalEntries;
        //List used to store objects in format required by Acumatica
        List<MyStoreContract.JournalTransactionDetail> myJournalTransactionsFormatted = new List<MyStoreContract.JournalTransactionDetail>();

        try
        {


            //Creating a header and returning a batch value to be used for all  line iterations. 
            JournalEntry myHeaderJournalEntryContract = allBatchItems.First();
            string myBatchNumberToProcess = AddGLBatchHeaderContractTEST(soapClient, myHeaderJournalEntryContract);

            // Do something with then n number of items defined in processing subBatch size  or remaining items if smaller
            foreach (JournalEntry je in allBatchItems)
            {
                //Moving the items in each batch from the original unformatted list to the formatted list one at a time 
                myJournalTransactionsFormatted.Add(new MyStoreContract.JournalTransactionDetail
                {
                    BranchID = new MyStoreContract.StringValue { Value = je.Branch },
                    Account = new MyStoreContract.StringValue { Value = je.Account },
                    Subaccount = new MyStoreContract.StringValue { Value = je.Subaccount },
                    ReferenceNbr = new MyStoreContract.StringValue { Value = je.RefNumber },
                    DebitAmount = new MyStoreContract.DecimalValue { Value = je.DebitAmount },
                    CreditAmount = new MyStoreContract.DecimalValue { Value = je.CreditAmount },
                    TransactionDescription = new MyStoreContract.StringValue { Value = je.TransactionDescription },
                    UsrTransactionTime = new MyStoreContract.StringValue { Value = je.UsrTransactionTime },
                    UsrTransactionType = new MyStoreContract.StringValue { Value = je.UsrTransactionType },
                    UsrTranSequence = new MyStoreContract.StringValue { Value = je.UsrTranSequence },
                    UsrTellerID = new MyStoreContract.StringValue { Value = je.UsrTellerID }
                });
            }


            //Specify the values of a new Jornal Entry using all the collected elements from the batch(list) created 
            MyStoreContract.JournalTransaction journalToBeCreated = new MyStoreContract.JournalTransaction
            {
                //Header data and details added by list generated by loop
                BatchNbr = new MyStoreContract.StringSearch { Value = myBatchNumberToProcess }, //This is one of two lines used to lookup/search the batch needing to be updated
                Module = new MyStoreContract.StringSearch { Value = myModuleForBatchLookup }, //This is one of two lines used to lookup/search the batch needing to be updated
                Details = myJournalTransactionsFormatted.ToArray() // this is the line adding the array containing all the line details
            };

            soapClient.Put(journalToBeCreated);


            Console.WriteLine("Added " + allBatchItems.Count.ToString() + " line transactions");
            Console.WriteLine();
            Console.WriteLine("Press any key to continue");
            Console.ReadLine();



        }

        catch (Exception e)
        {
            Console.WriteLine("The following error was encountered and all entries for this batch need to be logged in error table");
            Console.WriteLine();
            Console.WriteLine(e.Message);
            Console.WriteLine();
            Console.WriteLine("Press any key to continue");
            Console.ReadLine();
        }




    }


    public static string AddGLBatchHeaderContractTEST(MyStoreContract.DefaultSoapClient soapClient, JournalEntry je)
    {

        try
        {

            //Specify the values of a new Jornal Entry Batch header
            MyStoreContract.JournalTransaction journalToBeCreated = new MyStoreContract.JournalTransaction
            {
                //Header data
                BranchID = new MyStoreContract.StringValue { Value = "PRODWHOLE" }, //This is the default branch
                TransactionDate = new MyStoreContract.DateTimeValue { Value = je.TransactionDate.AddDays(-1) }, //Reduced 1 day from the batch
                CurrencyID = new MyStoreContract.StringValue { Value = je.CurrencyCode }, //Currency to be used for the batch
                Description = new MyStoreContract.StringValue { Value = je.TransactionDescription },
                Hold = new MyStoreContract.BooleanValue { Value = true }
            };

            //Create a Journal Entry with the specified values    

            MyStoreContract.JournalTransaction newJournalTransaction = (MyStoreContract.JournalTransaction)soapClient.Put(journalToBeCreated);
            string myBatchToProcess = newJournalTransaction.BatchNbr.Value;

            return myBatchToProcess;

        }
        catch (Exception e)
        {
            Console.WriteLine("Error was caught while trying to create the header for the batch...");
            Console.WriteLine();
            Console.WriteLine(e);
            Console.WriteLine();

            return null;
        }


    }

Мой пользовательский класс для устаревших системных позиций, который мне затем нужно отформатироватьв формат Acumatica:

            class JournalEntry
            {

                public DateTime TransactionDate { get; set; }
                public string CurrencyCode { get; set; }
                public string Description { get; set; }
                public string Branch { get; set; }
                public string Account { get; set; }
                public string Subaccount { get; set; }
                public string RefNumber { get; set; }
                public decimal DebitAmount { get; set; }
                public decimal CreditAmount { get; set; }
                public string TransactionDescription { get; set; }
                //Added custom fields for customer
                public string UsrTellerID { get; set; }
                public string UsrTransactionType { get; set; }
                public string UsrTransactionTime { get; set; }
                public string UsrTranSequence { get; set; }
                //Adding original file data for the line
                public string FileLineData { get; set; }
        }

Я попробовал подход Юрия, описанный ниже, но мои настраиваемые поля не обновляются.Только стандартные поля обновляются.Какую команду я должен использовать для обновления расширенных (пользовательских) полей.Смотрите код ниже:

                  //Here I create instance of GLTran
                    GLTran row = graph.GLTranModuleBatNbr.Cache.CreateInstance() as GLTran;


                    //here I get a handle to graph extension GLTranExt to be able to use the added fields.
                    var rowExt = row.GetExtension<GLTranExt>();


                    row = graph.GLTranModuleBatNbr.Insert(row);

                    graph.GLTranModuleBatNbr.Cache.SetValueExt(row, "AccountID", JE.Account);
                    graph.GLTranModuleBatNbr.Cache.SetValueExt(row, "SubID", JE.Subaccount);
                    row.TranDesc = "my line description"; 
                    row.Qty = 1.0m;
                    row.CuryDebitAmt = (JE.DebitAmount);
                    row.CuryCreditAmt = (JE.CreditAmount);
                    rowExt.UsrTellerID = "Test teller";
                    rowExt.UsrTransactionTime = "Test Transaction Time";
                    rowExt.UsrTransactionType = "Test Transaction Type";
                    rowExt.UsrTranSequence = "Test Transaction Sequence";



                    row = graph.GLTranModuleBatNbr.Update(row);

                    graph.Actions.PressSave();

1 Ответ

0 голосов
/ 02 декабря 2018

В многопоточном импорте заказов на продажу. У меня 18000 строк в час (4 ядра, 32 ГБ ОЗУ).Таким образом, ваши 25000 очень похожи на то, что я получаю (один заказ на продажу имел от 1 до 6 строк).Для второй ссылки, которую вы указали, какими были параметры вашего вызова API, сколько было ваших экземпляров Acumatica (ЦП, ОЗУ, параметры SQL Server)?

Я предлагаю вам рассмотреть масштабирование Acumatica по горизонтали., а также масштабировать вашу базу данных с помощью шардинга SQL.

Редактировать Если вам нужна одна партия GL с 25000 строками, я предлагаю вам следующий обходной путь:

  1. Создайте еще одну страницу Acumatica с текстовым полем и кнопкой «Импорт».
  2. В коде кнопки «Импорт»
    2.1 считывает информацию о текстовом поле в формате xml (или JSON)
    2.2 Создание экземпляра GL Graph
    2.3 Вставка через Graph необходимого количества (в вашем случае 25000) строк
    2.4 Вызов графу. PressSave ()

  3. Отправляйте ваш веб-API не в GL Batch, а на созданную вами страницу.

...