How to?

Como criar um serviço de Picking?

A criação de um serviço de picking é descrita em 3 etapas essenciais: Modelo do Serviço, Modelo da Apresentação e desenvolvimento de Custom Code.

Modelo de Serviços

  • Criar um serviço do tipo Picking
  • Criar um Main e Detail Resource e ligar ao serviço
  • Nas propriedades do serviço definir a Main e Detail resource
  • Criar uma Service Action e ligar ao serviço

Nota: Na Main Resource é obrigatório adicionar um atributo com o nome "SelectedCount" do tipo number com as propriedades "Required" e "Read-only" a true.

Na Detail Resource é obrigatório criar um atributo com o nome "Selected" do tipo boolean com a propriedade "Required" a true e "Read-only" a false.

Exemplo

Modelo de Serviços - modelação serviço picking

Nota: Na Main Resource(Expense Payment) foram adicionados os atributos StartDate e EndDate para serem utilizados futuramente como filtros

Modelo de Apresentação

  • Criar um Editor Operation (definir propriedades Service Operation e Process Operation para apontar para o serviço de picking criado)
  • Adicionar uma Main Action no Editor Operation (nas propriedades colocar na service action o serviço de picking)
  • Criar um Editor View , ligar ao Editor Operation
  • Editar o layout da View (Adicionar os campos necessários: Cabeçalho e Grid)

Exemplo

Modelo de Serviços - modelação serviço picking - apresentação Modelo de Serviços - modelação serviço picking - apresentação - layout

Custom Code

Após Transformar a solução são gerados 2 métodos para desenvolver a lógica do picking

Devolver Linhas para o Picking

O método para devolver as linhas encontra-se em Primavera.[NomeModulo].Domain.Business.Managers.Base.[NomeServiçoPicking]ManagerBase

Serviço Picking- custom code - get batch list

Este método devolve um Tuplo com um IEnumerable das linhas e um inteiro com o número total de linhas

Através dos parâmetros que o método recebe, conseguimos paginar a lista e aplicar o filtros(caso existam) que vêm no objeto MainResource(neste caso ExpensePayment)

  • Criar a classe Manager para o serviço de picking em Primavera.[NomeModulo].Domain.CustomCode.Managers.[NomeServiçoPicking]Manager.cs

  • Dentro desta classe, fazer override do método GetBatchPageListAsync(...)

Nota: No caso de criar uma classe nova, o namespace desta classe deve ser alterado para conter apenas até Primavera.[NomeModulo].Domain

Exemplo

Neste exemplo, são devolvidas linhas do tipo ExpenseLine que não estão contabilizadas (esta condição é aplicada no método GetExpensesToAccount)

Serviço Picking - custom code - exemplo - get batch list

Processar linhas selecionadas no Picking

O método gerado para processar as linhas selecionadas na página de picking encontrasse em Primavera.[NomeModulo].Services.Controllers.[NomeModulo]On[ServiceAction]Async

Serviço Picking- custom code - on process - Fazer override deste método em Primavera.[NomeModulo].Services.Controllers.CustomCode.Controllers.[NomeModulo][ServiçoPicking]Controller.cs

Nota: No caso de criar uma classe nova, o namespace desta classe deve ser alterado para conter apenas até Primavera.[NomeModulo].Services.Controllers

Abaixo é demonstrado um exemplo de um picking que constrói uma nova entidade

Exemplo

Nesta exemplo, o objetivo é copiar as linhas selecionadas(no picking) do tipo ExpenseLine para um novo documento do tipo AccountedExpense/AccountedExpenseLines, que é um novo objeto Master/Detail que pretende armazenar as ExpenseLines que foram contabilizadas.

Após o utilizador carregar em Process na página de Picking, será redirecionado para uma nova página onde vai construir um documento do tipo AccountedExpense (já com as linhas que foram selecionadas no picking adicionadas a este novo documento e "traduzidas" em AccountedExpenseLines).

Estrutura de entidades (Master/Detail)

Serviço Picking- custom code - on process - entidades

Método OnProcessExpensePaymentAsync


protected override async Task<HttpResponseMessage> OnProcessExpensePaymentAsync(Guid id, [FromBody] string version)
{
    using (TransactionScope transaction = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled))
    {
        HttpResponseMessage returnResponse;

        // Get the draft of the picking operation.
        DraftModel<ExpensePayment> draft = await this.DraftsService.GetAsync<ExpensePayment>(id);

        if (draft != null)
        {
            using (ProcessExpensePaymentsServiceManager processExpensePaymentsManager = new ProcessExpensePaymentsServiceManager())
            {
                // Create a new AccountedExpense based on the picked lines.
                AccountedExpense accountedExpense = await processExpensePaymentsManager.CreateAccountedExpenseFromExpensePaymentAsync(draft.Resource);

                // Map AccountedExpense entity to AccountedExpenseResource.
                AccountedExpenseResource accountedExpenseResource = accountedExpense.ToAccountedExpenseResource();

                // Create a draft with the mapped resource.
                await this.DraftsService.CreateAsync<AccountedExpenseResource>(accountedExpenseResource, DraftOperationType.Create, DraftState.Edit, "BusinessCore_CreateAccountedExpense");

                // Create return navigate action to AccountedExpense draft
                ReturnNavigateAction action = new ReturnNavigateAction()
                {
                    ModuleName = "businesscore",
                    NavigationType = NavigateType.Navigate,
                    ServiceName = "accountedExpenses",
                    OperationName = "createaccountedexpense",
                    OriginDraftDiscarded = false,
                    Parameters = new List<NavigateParameter>()
                    {
                        new NavigateParameter() { Property = "id", Value = accountedExpense.Id.ToString() }
                    }
                };

                // Create response.
                returnResponse = this.Request.CreateResponse(HttpStatusCode.OK, action);

                // Delete Process Order Draft.
                await this.DraftsService.DeleteDraftByKeyAsync<ExpensePayment>(id);
            }
        }
        else
        {
            returnResponse = this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, new HttpError(Properties.Resources.RES_Validation_DraftNotFound));
        }

        //  Complete the transaction.
        transaction.Complete();

        return returnResponse;
    }
}

Método que constrói a entidade AccountedExpense

namespace Primavera.BusinessCore.Domain
{
    internal partial class AccountedExpensesServiceManager
    {
        internal async Task<AccountedExpense> CreateAccountecExpenseFromExpensePaymentAsync(ExpensePayment resource)
        {
            // Create a new AccountedExpense object with the default values.
            AccountedExpense document = await this.CreateAccountedExpenseAsync();

            // Get selected ExpensePayment request ids.
            IEnumerable<Guid> requestIds = resource.ExpensePaymentLines.Select(s => s.ExpenseLineId);


            // Get selected Expenses
            IEnumerable<ExpenseLine> selectedExpenses = await this.Manager.EntityContext.ExpenseLines                
                .Where(s => requestIds.Contains(s.Id))
                .ToListAsync();

            document.Date = DateTime.Now;
            document.Currency = selectedExpenses.FirstOrDefault().Currency;
            document.AccountedExpenseLines = new List<AccountedExpenseLine>();
            foreach(var item in selectedExpenses)
            {

                document.AccountedExpenseLines.Add(new AccountedExpenseLine()
                {
                    ExpenseLineId = item.Id,
                    Amount = item.Amount,
                    CurrencyId = item.CurrencyId,
                    ReportingCurrencyId = item.ReportingCurrencyId,
                    BaseCurrencyId = item.BaseCurrencyId,
                    Date = item.Date,
                    Description = item.Description,
                    ExternalDoc = item.ExternalDoc,
                    ExpenseType = item.ExpenseType
                });
            }

            return document;
        }

    }
}