bgmulinari / B1SLayer

A lightweight SAP Business One Service Layer client for .NET
MIT License
124 stars 42 forks source link

Falha no Response do BATCH quando existe TRANSACTIONNOTIFICATION #18

Closed nilton-dev closed 2 years ago

nilton-dev commented 2 years ago

Boa tarde,

Estamos com dificuldade em pegar o response quando é uma resposta da TRANSACTIONNOTIFICATION...

Apresenta uma exceção e não retorna a mensagem: ---> System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.IO.IOException: The response ended prematurely. at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpClient.SendAsyncCore(HttpRequestMessage request, HttpCompletionOption completionOption, Boolean async, Boolean emitTelemetryStartStop, CancellationToken cancellationToken) at Flurl.Http.FlurlRequest.SendAsync(HttpMethod verb, HttpContent content, CancellationToken cancellationToken, HttpCompletionOption completionOption) --- End of inner exception stack trace --- at B1SLayer.SLConnection.ExecuteRequest[T](Func1 action) at B1SLayer.SLConnection.PostBatchAsync(IEnumerable1 requests, Boolean singleChangeSet)

Alguma sugestão para resolvermos essa questão?

Obs. Apenas quando é um retorno de TRANSACTIONNOTIFICATION que não conseguimos capturar!

bgmulinari commented 2 years ago

Olá, @nilton-dev. Vejo que está enviando uma requisição em batch. Poderia compartilhar o trecho de código com as requisições contidas nesse batch para que eu possa tentar simular o erro, por favor?

Se possível, também compartilhar qual validação está sendo realizada na transaction (código SQL).

nilton-dev commented 2 years ago

Olá @bgmulinari,

Como realizei alguns testes e obtive o mesmo resultado, para facilitar a análise vou te passar apenas um exemplo bem simples onde tenho o mesmo erro quando há uma transaction no meio do caminho:

Exemplo de batch de atualização de item

public static async Task TestarMetodoSimplificado()
{
  try
  {
    var serviceLayer = ServiceLayer.Connection;
    List<SLBatchRequest> slBatchRequests = new List<SLBatchRequest>();
    slBatchRequests.Add(new SLBatchRequest(new System.Net.Http.HttpMethod("PATCH"), "Items('CON-5')", new { ItemName = "Prestacao de Servico de Consultoria (B1SLayer)" }));
    slBatchRequests.Add(new SLBatchRequest(new System.Net.Http.HttpMethod("PATCH"), "Items('LIC-4')", new { ItemName = "Licenca de Software (B1SLayer)" })); //item with error
    slBatchRequests.Add(new SLBatchRequest(new System.Net.Http.HttpMethod("PATCH"), "Items('MAN-1')", new { ItemName = "Manutencao de Software (B1SLayer)" }));
    System.Net.Http.HttpResponseMessage[] batchResult = null;
    batchResult = await serviceLayer.PostBatchAsync(slBatchRequests, false);
    for (int i = 0; i < batchResult.Length; i++)
    {
      Console.WriteLine("Requisição: " + (i + 1) + " " + batchResult[i].StatusCode + " " + batchResult[i].IsSuccessStatusCode);
    }
  }
  catch (Exception e)
  {
    Console.WriteLine(e);
  }
}

Esse é um exemplo simples da transaction que estou usando só pra teste:

IF :object_type = '4' AND :transaction_type IN ('A','U') THEN

    error := 1001;
    error_message := 'Teste validação transaction';

END IF;

Obs. Quando eu faço essa mesma requisição de batch mas por outro meio, exmeplo Insominia ou Postman eu tenho o retorno da transaction normalmente....

Desde já agradeço,

vlw

bgmulinari commented 2 years ago

Obrigado, @nilton-dev. Vou analisar e te retorno assim que possível. Outra coisa, qual versão do SAP B1 está utilizando?

nilton-dev commented 2 years ago

Obrigado, @nilton-dev. Vou analisar e te retorno assim que possível. Outra coisa, qual versão do SAP B1 está utilizando?

@bgmulinari estou utilizando: SAP 10 - PL 02

nilton-dev commented 2 years ago

Boa tarde @bgmulinari ,

Conseguiu avaliar com as informações enviadas? Precisa de mais algum detalhe?

Caso precise de mais alguma que ajude na análise me informe,

Obrigado,

bgmulinari commented 2 years ago

Olá, @nilton-dev. Desculpe a demora no retorno.

Eu não consegui simular seu problema em meu ambiente versão 10 FP 2111 (único ambiente versão 10 que tenho acesso no momento). Fiz o seguinte teste com o mesmo trecho de transaction que você forneceu:

var req1 = new SLBatchRequest(
    HttpMethod.Patch,
    "Items('A000001')",
    new { ItemName = "Teste@" });

HttpResponseMessage[] batchResult = await serviceLayer.PostBatchAsync(req1);

foreach (var result in batchResult)
{
    Console.WriteLine(result.StatusCode);
    Console.WriteLine(result.Content.ReadAsStringAsync().Result);
}

Abaixo o resultado impresso no console:

BadRequest
{
   "error" : {
      "code" : -1116,
      "message" : {
         "lang" : "en-us",
         "value" : "(1001) Teste validação transaction"
      }
   }
}

Como pode ver, o resultado foi como o esperado. Sugiro verificar na sua Exception se a mesma possui uma InnerException detalhando melhor o motivo do problema.

Todavia eu pesquisei um pouco nas release notes das versões posteriores à versão 10 PL 02 e encontrei a nota 3058760, que descreve um problema na Service Layer muito semelhante ao que você relata. O problema aparentemente foi corrigido na versão 10.0 FP 2105, então talvez você precise verificar a possibilidade de atualizar a versão do B1.

Nota em PDF, caso não possua acesso ao link: 3058760_E_20220704.pdf

nilton-dev commented 2 years ago

@bgmulinari obrigado pelo retorno...

Realmente testei exatamente conforme enviou e me dá erro.

Olhando a nota que enviou, realmente parece muito com meu cenário, eu fiz alguns testes e descobri que o que está "atrapalhando" é o changeset no meio do caminho, se eu faço a requisição conforme abaixo via Insommia ou Postman, tenho sucesso com o retorno Transaction:

--batch_2b68f07a-f0b7-4955-a04f-8d710df19c02
Content-Type: application/http
Content-Transfer-Encoding:binary 

PATCH /b1s/v1/Items('CON-5')
Content-Type: application/json

{"ItemName": "Prestacao de Servico de Consultoria (B1SLayer)"}

--batch_2b68f07a-f0b7-4955-a04f-8d710df19c02--

Porém quando envio nesse formato aí eu tenho erro de Proxy Error

--batch_2b68f07a-f0b7-4955-a04f-8d710df19c02
Content-Type:multipart/mixed; boundary=changeset_ff6875c3-cdd4-4822-aacb-c0f2702d7cd0

--changeset_ff6875c3-cdd4-4822-aacb-c0f2702d7cd0
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1

PATCH /b1s/v1/Items('CON-5')
Content-Type: application/json

{"ItemName": "Prestacao de Servico de Consultoria (B1SLayer)"}

--changeset_ff6875c3-cdd4-4822-aacb-c0f2702d7cd0--
--batch_2b68f07a-f0b7-4955-a04f-8d710df19c02--

Em paralelo eu verificar uma possível atualização, teria alguma sugestão de contramedida para contornar essa situação?

bgmulinari commented 2 years ago

Olá, @nilton-dev. Um possível contorno seria baixar o fonte do B1SLayer e adicioná-lo ao seu projeto como referência de projeto. Dessa forma, você pode alterar o funcionamento da biblioteca, adequando-a da maneira que desejar.

Para o seu problema, acredito que você precisaria ajustar o método BuildMixedMultipartContent da classe SLConnection, que é responsável por construir o MultipartContent das requisições em batch.

nilton-dev commented 2 years ago

Boa tarde @bgmulinari ,

Apenas para lhe dar um feedback, realizei o Upgrade do sap para versão FP2111 e tudo funcionou perfeitamente!

Realmente era um problema na PL02.

Muito obrigado pela ajuda...

bgmulinari commented 2 years ago

Boa tarde, @nilton-dev. Que bom que conseguiu resolver.

Estou fechando a issue, mas qualquer coisa pode avisar que reabro.

nilton-dev commented 2 years ago

Obrigado @bgmulinari ...

Apenas uma dúvida, utilizo o batch para o conceito de "carga" mesmo, ou seja mandar de uma vez uma quantidade de requisições, nesse caso estou postando Nota Fiscais, fiz um teste de deixar uma "transaction" bloquear apenas um dos documentos que constam no Batch, porém notei que quando temo um bad request em qualquer documento do bacth ele não segue com os demais, o que pode ser?

Eu já tentei alterar o parâmetro singleChangeSet para false e true e ambos o mesmo resultado...

bgmulinari commented 2 years ago

@nilton-dev, as requisições em batch são similares em conceito à uma transaction no banco de dados. O comportamento da Service Layer é se alguma requisição dentro de um change set falhar, todo o change set é revertido (rollback).

Por padrão, o B1SLayer enviará todas as requisições do seu batch em um único change set (singleChangeSet = true), isso significa que se qualquer requisição deste batch não for bem sucedida, será feito um rollback automático de tudo contido neste batch, ou seja, é como se nenhuma requisição tivesse sido feita.

Agora, se você especificar o parâmetro singleChangeSet como false, cada requisição do seu batch será enviada em um change set separado. Dessa forma, caso alguma requisição do batch não for bem sucedida, não ocorrerá o rollback das requisições anteriores à requisição com erro, porém todas as requisições subsequentes à requisição com erro serão ignoradas.

Se você precisa que um grande volume de requisições seja feito de forma a ignorar as que falharem, talvez requisição batch não seja a melhor escolha. O uso de requisições batch se faz útil justamente quando você precisa desse comportamento de rollback.

A sessão "Batch Operations" do manual da Service Layer entra em mais detalhes sobre isso, portanto recomendo a leitura.