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
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
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
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)

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
- 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)
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;
}
}
}