Compartilhar via


Capítulo 7: Adicionando funcionalidade ao aplicativo

Kiana e Maria estão animadas para mostrar o aplicativo de gerenciamento de inventário para Caleb, o técnico de campo. Caleb gosta, mas sugere adicionar algumas funcionalidades extras de interface do usuário para torná-lo mais fácil de usar. Especificamente, Caleb gostaria de ser capaz de:

  • Adicionar uma fotografia do trabalho realizado em uma caldeira ou uma unidade de ar-condicionado e adicioná-la aos detalhes do compromisso na tela Editar Compromisso. Esta imagem pode ser útil como evidência documental dos reparos realizados. Atualmente, a tela Editar Compromisso permite que o usuário adicione uma imagem ao compromisso, mas a imagem não foi salva porque este recurso ainda não foi totalmente implementado. O motivo dessa omissão é que Kiana e Preeti precisam determinar o melhor local para armazenar os dados da imagem. Caleb gostaria que essa funcionalidade fosse adicionada o mais rápido possível.

  • Exiba um histórico completo de compromissos de um cliente para rastrear os reparos solicitados e monitorar quaisquer problemas em andamento que possam exigir que os técnicos sejam chamados repetidamente.

  • Encomende as peças na tela Detalhes da Peça.

Além disso, o controle de imagem na tela Detalhes da Peça exibe as imagens armazenadas em uma URL especificada. Atualmente, as URLs nos dados são simplesmente espaços reservados. Assim como as fotografias da tela de compromissos, Kiana e Preeti precisam determinar o melhor local para armazenar as imagens para que estejam disponíveis para o aplicativo.

Adicionar uma fotografia a um compromisso

As fotografias precisam ser armazenadas em um local acessível pelo aplicativo. Por motivos de desempenho e segurança, Preeti não quer que as fotografias sejam salvas no OneDrive ou no Banco de Dados SQL do Azure. Em vez disso, eles decidem usar o Armazenamento de Blobs do Azure. O Armazenamento de Blobs é otimizado para armazenar grandes objetos binários, é robusto e tem segurança integrada. O Power Apps tem um conector que permite o acesso ao Armazenamento de Blobs. Maria sugere adicionar uma nova tela para fotografias, melhorando a experiência do usuário para Caleb.

Mais informações: Armazenamento de Blobs do Azure

Preeti cria a conta de Armazenamento de Blobs do portal do Azure seguindo estas etapas:

  1. No Portal do Azure e, na Página Inicial, selecione + Criar um recurso. Na caixa de texto Pesquisar no Marketplace, insira Conta de armazenamento e, em seguida, selecione Inserir.

    Pesquisa do Azure Marketplace.

  2. Na página Conta de armazenamento, selecione Criar.

  3. Na página Criar conta de armazenamento, insira os seguintes detalhes e selecione Analisar + criar:

    • Assinatura: selecione sua assinatura
    • Grupo de recursos: webapi_rg
    • Nome da conta de armazenamento: forneça um nome exclusivo globalmente e anote-o para mais tarde
    • Local: selecione seu local mais próximo
    • Desempenho: Standard
    • Tipo de conta: BlobStorage
    • Replicação: RA-GRS

    Crie a conta de armazenamento do Azure.

  4. Na página de validação, selecione Criar e aguarde o provisionamento da conta de armazenamento.

  5. Vá para a página da nova conta de armazenamento.

  6. Na página Visão geral, selecione Contêineres.

    Página de visão geral da conta de armazenamento.

  7. Na página Contêineres, selecione + Contêiner. Crie um novo contêiner chamado fotos e, em seguida, selecione Criar. Altere o Nível de acesso público para Blob.

    Crie o contêiner de fotos.

  8. Retorne à página Visão geral da conta de armazenamento e, em configurações, selecione Chaves de acesso. Na página Chaves de acesso, selecione Mostrar as chaves. Anote o valor da chave para key1.

    Chaves de acesso à conta de armazenamento.

Preeti fornece o nome da conta de armazenamento e a chave a Kiana, que usa essas informações para criar um conector personalizado para o aplicativo seguindo estas etapas:

  1. Entre no Power Apps.

  2. No painel esquerdo, expanda Dados, e escolha Conexões. As conexões existentes usadas pelo aplicativo devem ser listadas. Selecione +Nova conexão.

    Página de conexões do Power Apps.

  3. Na página Nova conexão, role para baixo, selecione Conexões, Armazenamento de Blobs do Azure e, em seguida, Criar.

    Selecione o conector do Armazenamento de Blobs do Azure.

  4. Na caixa de diálogo Armazenamento de Blobs do Azure, insira o nome da conta de armazenamento e a chave de acesso que Preeti forneceu e selecione Criar.

    Insira as credenciais de armazenamento.

  5. Aguarde enquanto a nova conexão é criada. Ela deve aparecer na lista de conexões.

Maria pode usar essa conexão com o Armazenamento de Blobs no aplicativo para salvar e recuperar imagens fotográficas. A primeira tarefa de Maria é adicionar a conexão ao aplicativo seguindo estas etapas:

  1. Abra o aplicativo VanArsdelApp para edição no Power Apps Studio.

  2. No painel Dados, selecione Adicionar dados, procure o conector Armazenamento de Blobs do Azure e, em seguida, selecione o conector.

    Pesquise o conector de Armazenamento de Blobs.

  3. Na caixa de diálogo Armazenamento de Blobs do Azure, selecione o conector Armazenamento de Blobs do Azure para adicioná-lo ao seu aplicativo.

    Adicione uma conexão de Armazenamento de Blobs.

A próxima tarefa de Maria é adicionar uma tela que permita a um técnico ou um engenheiro salvar uma fotografia. Maria decide adicionar uma nova tela com um controle de imagem. Quando o aplicativo é executado em um dispositivo móvel, esse controle pode ser integrado à câmera para permitir que o técnico tire uma fotografia. Em outros dispositivos, este controle solicita que o usuário carregue um arquivo de imagem. Maria adiciona um link para essa nova tela na tela EditAppointment seguindo estas etapas:

  1. No menu Inserir, selecione Nova tela e, em seguida, selecione o modelo Rolável.

    Nova tela do modelo de rolagem.

  2. No painel Exibição de árvore, selecione a nova tela e renomeie-a como TakePhoto.

  3. Altere a propriedade Texto do controle LblAppNameX nesta tela para Tirar uma fotografia.

  4. Exclua o controle CanvasX da tela.

  5. No menu Inserir, na lista suspensa Mídia, selecione Adicionar foto para criar um novo controle de imagem.

    Adicione um controle de imagem.

    Observação

    O controle de imagem é, na verdade, um componente personalizado composto que permite ao usuário adicionar uma imagem à tela e exibir os resultados.

  6. Redimensione e reposicione o controle de imagem para ocupar o corpo da tela.

  7. No painel Exibição de árvore, selecione o controle IconBackarrowX na tela AppointmentDetails e, em seguida, selecione Copiar.

    Copie o controle de seta para voltar.

  8. No menu Exibição de árvore, clique com o botão direito na tela TakePhoto e, em seguida, selecione Colar. O controle IconBackArrowX será adicionado à tela.

    Cole o controle de seta para voltar na tela TakePhoto.

  9. Mova o controle IconBackArrowX para o canto superior esquerdo da barra de cabeçalho.

  10. No painel Exibição de árvore, selecione o controle IconBackArrowX na tela TakePhoto. No painel direito, na guia Avançado, modifique a propriedade de ação OnSelect para Navigate(EditAppointment, ScreenTransition.None).

  11. Adicione um novo controle de ícone Salvar no canto superior direito da barra de cabeçalho. Defina a propriedade Visível deste controle como If(IsBlank(AddMediaButton1.Media), false, true).

    Esta configuração torna o ícone Salvar invisível se o usuário não selecionou uma imagem.

    Adicione o controle de ícone de salvamento.

  12. Altere a fórmula na propriedade de ação OnSelect do controle do ícone Salvar para a seguinte.

    Set(ImageID, GUID() & ".jpg");
    
    AzureBlobStorage.CreateFile("photos", ImageID, AddMediaButton1.Media);
    
    Patch(appointmentsCollection, LookUp(appointmentsCollection,id=BrowseAppointmentsGallery.Selected.id), {imageUrl:"https://myappphotos.blob.core.windows.net/photos/" & ImageID});
    
    Navigate(EditAppointment,ScreenTransition.Cover);
    

    Substitua <storage account name> pelo nome da conta de armazenamento do Azure que Preeti criou.

    Este código carrega a imagem no contêiner fotos no Armazenamento de Blobs. Cada imagem recebe um nome de arquivo exclusivo. A função Patch atualiza a propriedade imageUrl no registro de compromissos com a URL da imagem no Armazenamento de Blobs.

  13. No painel Exibição de árvore, expanda o controle AddMediaWithImageX. Modifique a propriedade Image do controle UploadedImageX e defina-o como AppointmentImage.

    AppointmentImage é uma variável que será preenchida com uma imagem enviada pelo usuário ou como resultado de tirar uma fotografia. Você inicializará esta variável na tela EditAppointment mais tarde.

  14. No painel Exibição de árvore, selecione o controle AddMediaButtonX. Defina a propriedade UseMobileCamera desse controle como este true. Defina a propriedade de ação OnChange do controle como a seguir.

    Set(AppointmentImage, AddMediaButton1.Media)
    

    Esta fórmula muda a variável AppointmentImage para referenciar a nova imagem. O controle UploadedImageX exibirá esta imagem.

  15. No painel Exibição de árvore, selecione a tela EditAppointment.

  16. Expanda o controle EditFormX. No controle Image_DataCardX, remova o controle AddPictureX.

    Remova o controle AddPicture.

  17. Selecione o controle ImageX. Altere as seguintes propriedades:

    • Imagem: Parent.Default
    • X: 30
    • Y: DataCardKeyX.Y + DataCardKeyX.Height + 150 (em que DataCardKeyX é o cartão de dados que contém o controle ImageX)
    • Largura: Parent.Width - 60
    • Altura: 400

    Observação

    O controle da imagem será movido para o menu suspenso abaixo da parte inferior da tela, mas uma barra de rolagem será adicionada automaticamente para permitir que a imagem seja visualizada.

  18. Adicione um ícone Câmera ao cartão de dados, em seguida, posicione-o entre o rótulo Imagem e o controle ImageX. Altere o nome do controle para CameraIcon.

    Observação

    Certifique-se de selecionar o controle de ícone da câmera, não o controle de mídia da câmera.

    Adicione o ícone Câmera.

  19. Defina a propriedade de ação OnSelect do controle CameraIcon como a seguir.

    Set(AppointmentImage, SampleImage);
    
    Navigate(TakePhoto, ScreenTransition.None);
    

    Quando o usuário seleciona este ícone, ele vai para a tela TakePhoto, em que ele pode tirar uma foto ou fazer upload de uma imagem. A imagem inicial exibida será a imagem de exemplo padrão.

Para testar o aplicativo, faça o seguinte:

  1. No painel Exibição de árvore, selecione a tela Página Inicial.

  2. Selecione F5 para visualizar o aplicativo.

  3. Na tela Página Inicial, selecione Compromissos.

  4. Na tela de navegação, selecione qualquer compromisso.

  5. Na tela de detalhes do compromisso, selecione o ícone de edição no cabeçalho da tela.

  6. Na tela de edição, selecione o ícone Câmera para a imagem.

  7. Verifique se a tela Tirar uma foto aparece.

  8. Selecione Alterar imagem e carregue uma foto de sua escolha (ou tire uma foto, se você estiver executando o aplicativo em um dispositivo móvel).

  9. Selecione Salvar. Verifique se a imagem aparece na página de detalhes e selecione o ícone de verificação para salvar as alterações novamente no banco de dados.

  10. Feche a janela de visualização e retorne ao Power Apps Studio.

Exibindo imagens de peças

Tendo determinado que o Armazenamento de Blobs é um local ideal para salvar imagens associadas a compromissos, Preeti e Kiana decidem que devem usar a mesma abordagem para armazenar as imagens das peças. A principal vantagem dessa abordagem é que ela não requer nenhuma modificação no aplicativo. O aplicativo reutiliza a mesma conta de armazenamento e a mesma conexão. Como um exercício de migração separado, elas podem fazer o seguinte:

  1. Criar um novo contêiner de Armazenamento de Blobs.

  2. Fazer upload das imagens das peças para este contêiner.

  3. Altere as referências ImageUrl na tabela Peças no banco de dados InventoryDB para a URL de cada imagem.

O aplicativo escolherá a nova URL para cada imagem de peça automaticamente, e o controle Imagem na tela PartDetails exibirá a imagem.

Acompanhamento do histórico de compromissos de um cliente

Maria acha que a opção de visualizar rapidamente todo o histórico de visitas técnicas anteriores de um cliente pode ser adicionada ao aplicativo criando um componente personalizado. Trabalhando com Caleb em quais informações desejam ver, Maria esboça um design simples que inclui as anotações e a data de cada visita.

Dados do histórico de compromissos do cliente.

Olhando para os dados, Maria acredita que um controle de galeria é a melhor maneira de exibir os dados da tabela em uma tela.

Maria cria o componente personalizado da seguinte maneira:

  1. Usando o Power Apps Studio, no painel Exibição de árvore, selecione Componentes e, em seguida, selecione + Novo componente.

    Crie um novo componente.

    Um novo componente em branco chamado Component1 é criado. Renomeie o componente como DateHistoryComponent.

    Renomeie o componente.

  2. No menu Inserir, selecione Galeria e, em seguida, escolha o modelo de galeria Altura flexível em branco.

    Adicione um controle de galeria.

  3. Mova o controle da galeria e redimensione-o para preencher o componente personalizado.

  4. Selecione Adicionar um item do painel de inserção e, em seguida, selecione Rótulo de texto.

    Adicione um rótulo de texto ao componente.

  5. No painel Exibição de árvore, renomeie o controle de rótulo como NotesLabel. Defina a propriedade Estouro como Overflow.Scroll. Essa configuração permite que o controle exiba várias linhas de texto e permite que o usuário percorra o conteúdo. Defina as seguintes propriedades para que você possa posicionar e dimensionar o controle:

    • LineHeight: 2
    • X: 28
    • Y: 18
    • Largura: 574
    • Altura: 140
  6. Adicione um segundo rótulo de texto ao controle. Renomeie este controle como DateLabel e defina as seguintes propriedades:

    • LineHeight: 2
    • X: 28
    • Y: 174
    • Largura: 574
    • Altura: 70
  7. Para ver como o controle ficará quando inserido no aplicativo e exibido com seu tema, no painel Exibição de árvore, selecione DateHistoryComponent. No painel direito, na guia Avançado, selecione o campo Preencher e mude a cor para RGBA(0, 0, 0, 1).

    Exiba o componente.

  8. No painel Inserir, expanda Formas e adicione um controle Retângulo ao componente personalizado. Defina as seguintes propriedades para este controle:

    • X: 0
    • Y: 273
    • Largura: Parent.Width
    • Altura: 2

    Este controle atua como um separador entre os registros exibidos na galeria.

    Adicione um controle de retângulo.

Maria tem conhecimento sobre a adição de controles a telas e a criação de aplicativos com o Power Apps. No entanto, os componentes reutilizáveis não funcionam exatamente da mesma maneira. Kiana disse a Maria que, para poder usar dados em um componente personalizado, é necessário incluir algumas propriedades de entrada personalizadas adicionais. Kiana também explicou que Maria precisa fornecer dados de exemplo para essas propriedades para fazer referência aos campos de dados nos controles do componente, da seguinte maneira:

  1. No painel Exibição de árvore, selecione DateHistoryComponent. No painel direito, na guia Propriedades, selecione Nova propriedade personalizada.

    Nova propriedade personalizada.

  2. Na caixa de diálogo Nova propriedade personalizada, especifique os seguintes valores e selecione Criar:

    • Nome de exibição: Dados
    • Nome: Dados
    • Descrição: a tabela de compromissos para um cliente, mostrando as notas e as datas
    • Tipo da propriedade: Entrada
    • Tipo de dados: Tabela
    • Elevar OnReset quando o valor mudar: deixe em branco

    Novas propriedades personalizadas.

  3. Para alterar os dados de exemplo exibidos pelo controle, selecione a nova propriedade personalizada Dados. No campo de fórmula, insira Table({Notes: "Example notes field text.", 'Appointment Date': Text(Today())}).

    Altere os dados de exemplo.

  4. No painel Exibição de árvore, selecione o controle GalleryX em DateHistoryComponent e renomeie-o como AppointmentHistory.

  5. No painel direito, na guia Avançado, defina a propriedade Itens do controle da galeria AppointmentHistory como Parents.Data.

    Atualize a propriedade Itens para o controle da galeria.

  6. Selecione o controle NotesLabel. No painel direito da guia Avançado, altere a propriedade Texto para ThisItem.Notes e altere a propriedade Tamanho para 20.

    Observação

    A propriedade Tamanho especifica o tamanho da fonte para o texto exibido pelo controle.

  7. Selecione o controle DateLabel para alterar a propriedade Texto para ThisItem.'Appointment Date' e alterar a propriedade Tamanho para 20. Os campos no componente personalizado devem exibir os dados de exemplo.

    Componente personalizado com dados de exemplo.

O componente personalizado está completo. Maria cria uma nova tela para exibir o histórico de compromissos de um cliente usando este componente, da seguinte maneira:

  1. No painel Exibição de árvore, selecione a guia Telas.

  2. Expanda a tela BrowseAppointments, expanda o controle BrowseAppointmentsGallery e selecione o controle Body1_1. No menu Inserir, selecione Ícones e, em seguida, selecione o ícone Lista de detalhes.

    Adicione o ícone da lista de detalhes.

  3. Altere o nome do controle de ícone para ViewAppointments.

  4. No menu Exibição de árvore, selecione o controle BrowseAppointmentsGallery. No painel direito, na guia Avançado, altere a propriedade TemplateSize para 220. Aumentar essa propriedade expande o espaço disponível na galeria.

  5. Mova o ícone ViewAppointments para o espaço vazio abaixo do nome do cliente.

    Galeria de compromissos alterada.

  6. Selecione o controle de ícone ViewAppointments. Defina a propriedade de ação OnSelect com a fórmula a seguir.

    ClearCollect(customerAppointmentsCollection, FieldEngineerAPI.getapicustomeridappointments(ThisItem.customerId));
    
    Navigate(AppointmentsHistoryScreen, ScreenTransition.Fade)
    

    Esta fórmula preenche uma coleção chamada customerAppointmentsCollection com os compromissos para o cliente selecionado e, em seguida, transfere-a para AppointmentHistoryScreen para exibi-los. Você criará essa tela nas etapas a seguir.

  7. No menu Inserir, selecione Nova tela e, em seguida, selecione o modelo Rolável.

    Nova tela baseada no modelo de rolagem.

  8. Altere o nome da nova tela para AppointmentHistoryScreen.

  9. Exclua o controle CanvasX que foi adicionado a esta tela.

    Exclua o controle de tela.

  10. Selecione o controle LblAppNameX nesta tela. No painel direito, na guia Avançado, altere a propriedade Texto da seguinte forma.

    "Appointments History for " &  BrowseAppointmentsGallery.Selected.customer.name
    
  11. Defina as seguintes propriedades para o controle LblAppNameX para ajustar a posição e o tamanho:

    • X: 90
    • Y: 0
    • Largura: 550
    • Altura: 140
  12. Selecione o controle RectQuickActionBarX e defina a propriedade Altura como 140.

  13. Adicione um controle Ícone esquerdo ao cabeçalho da tela, à esquerda do título. Defina a propriedade de ação OnSelect para este controle como Navigate(BrowseAppointments, Transition.None).

    Tela AppointmentsHistory vazia.

  14. No menu Inserir, selecione Personalizado e, em seguida, selecione DateHistoryComponent.

    Adicione o componente DateHistory.

  15. Mova e redimensione o componente para que ele ocupe o corpo da tela, abaixo do título.

    Componente redimensionado.

  16. Defina as seguintes propriedades para este componente:

    • Dados: customerAppointmentsCollection
    • Data do compromisso: startDateTime
    • Anotações: notas
  17. Salve o aplicativo.

Para testar o aplicativo, faça o seguinte:

  1. No painel Exibição de árvore, selecione a tela Página Inicial.

  2. Selecione F5 para visualizar o aplicativo.

  3. Na tela Página Inicial, selecione Compromissos.

  4. Na tela de navegação, selecione o ícone Lista de detalhes para qualquer compromisso.

  5. Verifique se a tela Histórico de Compromissos do cliente selecionado é exibida.

  6. Feche a janela de visualização e retorne ao Power Apps Studio.

Pedidos de peças

Um requisito fundamental do sistema é permitir que um técnico faça o pedido de todas as peças necessárias durante uma visita ao cliente. Se as peças estiverem em estoque, deve ser possível agendar outra visita para concluir o reparo na próxima data conveniente para o cliente. Se as peças estiverem fora de estoque no momento e precisarem ser encomendadas, o técnico poderá informar ao cliente. Assim, Malik poderá marcar um compromisso com o cliente quando Maria receber o aviso de que as peças chegaram ao depósito.

A parte de reservas do aplicativo usa as tabelas no banco de dados InventoryDB mostrado na imagem a seguir. A tabela Pedidos contém informações sobre pedidos de peças. A tabela Reservas lista as solicitações de reserva que os técnicos e engenheiros fizeram para as peças. A tabela Engenheiros fornece o nome e o número de contato do engenheiro que fez a reserva, o que torna mais fácil para Maria, a gerente de estoque, consultar se necessário.

O modelo de dados de reservas.

Para oferecer suporte a esse recurso, Kiana precisa atualizar a API Web com um método que busca o número de itens reservados para uma peça especificada, da seguinte maneira:

  1. Abra o projeto de API Web FieldEngineerApi no Visual Studio Code.

  2. Adicione um arquivo chamado Order.cs para a pasta Modelos. Adicione o código a seguir a este arquivo. A classe Pedidos rastreia os detalhes dos pedidos feitos para as peças.

    using System;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace FieldEngineerApi.Models
    {
        public class Order 
        {
            [Key]
            public long Id { get; set; }
    
            public long BoilerPartId { get; set; }
    
            public BoilerPart BoilerPart { get; set; }
    
            public long Quantity { get; set; }
    
            [Column(TypeName = "money")]
            public decimal TotalPrice { get; set; }
    
            [Display(Name = "OrderedDate")]
            [DataType(DataType.DateTime)]
            [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")]
            public DateTime OrderedDateTime { get; set; }
    
            public bool Delivered { get; set; }
    
            [Display(Name = "DeliveredDate")]
            [DataType(DataType.DateTime)]
            [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")]
            public DateTime? DeliveredDateTime { get; set; }
        }
    }
    
  3. Adicione outro novo arquivo chamado Reservation.cs à pasta Modelos e adicione o seguinte código a este arquivo. A classe Reserva contém informações sobre o número de itens de uma determinada peça que estão atualmente reservados para outros clientes.

    using System;
    using System.ComponentModel.DataAnnotations;
    
    namespace FieldEngineerApi.Models
    {
        public class Reservation
        {
            [Key]
            public long Id { get; set; }
    
            public long BoilerPartId { get; set; }
    
            public BoilerPart BoilerPart { get; set; }
    
            public int NumberToReserve { get; set; }
    
            public string EngineerId { get; set; }
    
            public InventoryEngineer Engineer { get; set; }
        }
    }
    
  4. Adicione mais um arquivo, chamado InventoryEngineer.cs, à pasta Modelos, com o código a seguir. A classe InventoryEngineer registra quais engenheiros fizeram quais reservas.

    using System.ComponentModel.DataAnnotations;
    using System.Collections.Generic;
    
    namespace FieldEngineerApi.Models
    {
        public class InventoryEngineer
        {
            [Key]
            public string Id { get; set; }
    
            [Required]
            public string Name { get; set; }
    
            public string ContactNumber { get; set; }
    
            public List<Reservation> Reservations { get; set; }
        }
    }
    
  5. Abra o arquivo InventoryContext.cs na pasta Modelos e adicione as seguintes instruções à classe InventoryContext.

    public class InventoryContext : DbContext
    {
        public InventoryContext(DbContextOptions\<InventoryContext\> options)
            : base(options)
        {
    
        }
    
        public DbSet<BoilerPart> BoilerParts { get; set; }
        public DbSet<InventoryEngineer> Engineers { get; set; }
        public DbSet<Order> Orders { get; set; }
        public DbSet<Reservation> Reservations { get; set; }
    }
    
  6. Na janela Terminal no Visual Studio Code, execute os seguintes comandos para criar controladores para lidar com pedidos e reservas.

    dotnet aspnet-codegenerator controller ^
        -name OrdersController -async -api ^
        -m Order ^
        -dc InventoryContext -outDir Controllers
    
    dotnet aspnet-codegenerator controller ^
        -name ReservationsController -async -api ^
        -m Reservation ^
        -dc InventoryContext -outDir Controllers
    
  7. Abra o arquivo BoilerPartController.cs na pasta Controladores e adicione o método GetTotalReservations a seguir à classe BoilerPartsController.

    public class BoilerPartsController : ControllerBase
    {
        private readonly InventoryContext _context;
    
        public BoilerPartsController(InventoryContext context)
        {
            _context = context;
        }
    
        ...
    
        // GET: api/BoilerParts/5/Reserved 
        [HttpGet("{id}/Reserved")]
        public async Task<ActionResult<object>> GetTotalReservations(long id)
        { 
            var reservations = await _context
                .Reservations
                .Where(r => r.BoilerPartId == id) 
                .ToListAsync();
    
            int totalReservations = 0; 
    
            foreach(Reservation reservation in reservations) 
            { 
                totalReservations += reservation.NumberToReserve; 
            } 
    
            return new {id, totalReservations}; 
        }
        ...
    }
    
  8. Edite o arquivo OrdersController.cs e modifique o método PostOrder na classe OrdersController, conforme mostrado a seguir.

    [HttpPost]
    public async Task<ActionResult<Order>> PostOrder(long boilerPartId, int quantity)
    {
        var part = await _context.BoilerParts.FindAsync(boilerPartId);
    
        Order order = new Order 
        {
            BoilerPartId = boilerPartId,
            Quantity = quantity,
            OrderedDateTime = DateTime.Now,
            TotalPrice = quantity * part.Price
        };
    
        _context.Orders.Add(order);
        await _context.SaveChangesAsync();
    
        return CreatedAtAction("GetOrder", new { id = order.Id }, order);
    }
    
  9. Edite o arquivo ReservationsController.cs. Modifique o método PostReservation na classe ReservationsController, como a seguir.

    [HttpPost]
    public async Task<ActionResult<Reservation>> PostReservation(long boilerPartId, string engineerId, int quantityToReserve)
    {
        Reservation reservation = new Reservation 
        {
            BoilerPartId = boilerPartId,
            EngineerId = engineerId,
            NumberToReserve = quantityToReserve
        };
    
        _context.Reservations.Add(reservation);
        await _context.SaveChangesAsync();
    
        return CreatedAtAction("GetReservation", new { id = reservation.Id }, reservation);
    }
    
  10. Na janela Terminal, execute os seguintes comandos para criar e publicar a API Web pronta para implantação.

    dotnet build
    dotnet publish -c Release -o ./publish
    
  11. No Visual Studio Code, clique com o botão direito na pasta publicar e, em seguida, selecione Implantar no aplicativo Web.

Preeti agora pode atualizar o serviço de gerenciamento de API usado pelo aplicativo VanArsdel para refletir a API Web atualizada. Esta é uma alteração da falha; as operações existentes continuarão a funcionar, com a diferença de novos controladores e operações para fazer reservas e pedidos. Preeti executa as seguintes tarefas:

Observação

Preeti poderia ter escolhido excluir a API de Engenheiro de Campo existente e substituí-la por uma nova versão, mas essa abordagem corre o risco de falhas em quaisquer aplicativos existentes que possam estar usando a API. É uma prática melhor deixar a API existente no local e adicionar as modificações como uma revisão.

  1. No portal do Azure, vá para o serviço de gerenciamento de API.

  2. Na página Serviço de Gerenciamento de API, no painel esquerdo em APIs, selecione APIs.

  3. Selecione a API de Engenheiro de Campo, selecione o menu de reticências e, em seguida, selecione Adicionar revisão.

    Adicione uma revisão à API de Engenheiro de Campo.

  4. Na caixa de diálogo Criar uma nova revisão de Engenheiro de Campo, insira a descrição Operação GET e operações POST adicionadas para reservas e pedidos de peças e, em seguida, selecione Criar.

    Crie a revisão.

  5. Na página REVISÃO 2, selecione Projetar.

    Projete a revisão.

  6. Na página Projetar, selecione Adicionar operação. No painel FrontEnd, defina as seguintes propriedades e selecione Salvar. Esta operação é usada para recuperar o número de itens reservados para uma determinada peça da caldeira:

    • Nome de exibição: api/BoilerParts/{id}/Reserved
    • Nome: api-boilerparts-id-reserved
    • URL: GET api/BoilerParts/{id}/Reserved

    Adicione a operação de API reservada.

  7. Na guia Teste da nova operação, defina o parâmetro ID para um número de peça válido (o exemplo na imagem usa a peça 1) e, em seguida, selecione Enviar.

    Teste a API Web.

  8. Verifique se o teste foi bem-sucedido. A operação deve ser concluída com uma resposta HTTP 200 e um corpo que mostra o número de reservas do produto.

    A resposta do teste.

  9. Na página Projetar, selecione Adicionar operação. No painel FrontEnd, defina as seguintes propriedades (esta operação define solicitações POST para a criação de novos pedidos):

    • Nome de exibição: api/Orders - POST
    • Nome: api-orders-post
    • URL: POST api/Orders
  10. Na guia Consulta, selecione + Adicionar parâmetro, adicione os seguintes parâmetros e selecione Salvar:

    • Nome: boilerPartId, Descrição : Boiler Part ID, Tipo: long
    • Nome: quantity, Descrição : Quantity, Tipo: integer

    Adicione parâmetros à operação de consulta de gerenciamento de API.

  11. Selecione Adicionar operação novamente no painel FrontEnd e defina as seguintes propriedades (esta operação define solicitações POST para a criação de novas reservas):

    • Nome de exibição: api/Reservations - POST
    • Nome: api-reservations-post
    • URL: POST api/Reservations
  12. Na guia Consulta, adicione os seguintes parâmetros e selecione Salvar:

    • Nome: boilerPartId, Descrição: Boiler Part ID, Tipo: long
    • Nome: engineerId, Descrição: Engineer ID, Tipo: string
    • Nome: quantityToReserve, Descrição: Quantity to reserve, Tipo: integer
  13. Na guia Revisões, selecione a nova versão. No menu de reticências desta versão, selecione Tornar atual.

    Defina a versão atual para a revisão.

  14. Na caixa de diálogo Tornar a revisão atual, selecione Salvar.

  15. Abra outra página em seu navegador e acesse a URL https://<APIM name>.azure-api.net/api/boilerparts/1/reserved em que <APIM name> é o nome do seu serviço de API. Verifique se você obteve uma resposta semelhante à seguinte.

    {"id":1,"totalReservations":5}
    

A API Web atualizada já está disponível. Em teoria, Kiana poderia criar um novo conector personalizado para a API Web atualizada e adicioná-lo ao aplicativo. O aplicativo pode implementar sua própria lógica para determinar quantos itens do produto especificado estão em estoque atualmente, quantos estão reservados, comparar os resultados com o número de itens necessários, fazer um pedido de mais estoque, se necessário, ou reservar itens do estoque existente. No entanto, esse tipo de lógica é melhor implementado em um aplicativo lógico do Azure. O Power Apps pode chamar o aplicativo lógico por meio de um conector personalizado quando um técnico deseja reservar ou solicitar uma peça.

Para criar o aplicativo lógico, Kiana usa as seguintes etapas:

Observação

Para manter as coisas simples, o aplicativo lógico criado neste exemplo não é transacional. É possível que, entre verificar a disponibilidade de uma peça e fazer uma reserva, um usuário simultâneo faça uma reserva conflitante. Você poderia implementar a semântica transacional substituindo parte da lógica neste aplicativo lógico por um procedimento armazenado no banco de dados InventoryDB.

  1. No Portal do Azure e, na Página Inicial, selecione + Criar um recurso.

  2. Na caixa de texto Pesquisar no Marketplace, insira Aplicativo Lógico e, em seguida, selecione Inserir.

  3. Na página Aplicativo Lógico, selecione Criar.

    Crie o aplicativo lógico.

  4. Na página Criar um aplicativo lógico, insira os seguintes valores e selecione Analisar + criar:

    • Assinatura: selecione sua assinatura do Azure
    • Grupo de recursos: webapi_rg
    • Nome do aplicativo lógico: FieldEngineerPartsOrdering
    • Região: selecione o mesmo local que você usou para a API Web
    • Associar ao ambiente de serviço de integração: deixe em branco
    • Habilitar análise de logs: deixe em branco
  5. Na página de verificação, selecione Criar e aguarde enquanto o aplicativo lógico é implantado.

  6. Quando a implantação for concluída, selecione Acessar o recurso.

  7. Role a página Designer de Aplicativos Lógicos para baixo até a seção Modelos e selecione Aplicativo lógico em branco.

    Selecione o modelo de aplicativo lógico em branco.

  8. Na guia Tudo , selecione Solicitação na caixa de texto Conectores e gatilhos de pesquisa .

    Selecione o gatilho de solicitação. 

  9. Na guia Gatilhos, selecione Quando uma solicitação HTTP for recebida.

    Dispare quando uma solicitação HTTP for recebida.

  10. Na caixa Esquema JSON do Corpo da Solicitação, digite o seguinte esquema e selecione + Nova etapa.

    {
        "type": "object",
        "properties": {
            "boilerPartId": {
                "type": "integer"
            },
            "numberToReserve": {
                "type": "integer"
            },
            "engineerId": {
                "type": "string"
            }
        }
    }
    

    Esquema de solicitação de aplicativo lógico.

    Este esquema define o conteúdo da solicitação HTTP que o aplicativo lógico está esperando. O corpo da solicitação compreende o ID de uma peça da caldeira, o número de itens a serem reservados e o ID do engenheiro que está fazendo a solicitação. O aplicativo enviará essa solicitação quando um engenheiro quiser reservar uma peça.

  11. Na caixa Escolher uma operação, selecione Tudo e, em seguida, selecione HTTP.

    Selecione a opção de operação HTTP.

    O aplicativo lógico chamará a operação BoilerParts{id} da API Web para recuperar informações sobre a peça da caldeira fornecida pela solicitação do aplicativo.

  12. No painel Ações, selecione a ação HTTP.

    Selecione a opção de ação HTTP.

  13. Na caixa de ação HTTP, no menu de reticências, selecione Renomear e altere o nome da ação para CheckBoilerPart.

    Renomeie a ação HTTP.

  14. Defina as propriedades da ação HTTP da seguinte forma e, em seguida, selecione + Nova Etapa:

    • Método: GET
    • URI: https://<APIM name>.azure-api.net/api/boilerparts/, onde <APIM name> é o nome do seu serviço de Gerenciamento de API. Na caixa Conteúdo dinâmico desta URI, selecione boilerPartId na guia Conteúdo dinâmico

    Especifique o conteúdo dinâmico para ação HTTP.

  15. Na caixa Escolher uma operação, na caixa Conectores de pesquisa e ações, insira Analisar JSON e, em seguida, selecione a ação Analisar JSON.

    Selecione a ação Analisar JSON.

  16. Usando o menu de reticências para a ação Analisar JSON, renomeie a ação como ParseBoilerPart.

  17. Na caixa Conteúdo da ação ParseBoilerPart, na caixa Conteúdo Dinâmico, selecione Corpo. Na caixa Esquema, digite o seguinte esquema JSON e selecione + Nova etapa.

    {
        "type": "object",
        "properties": {
            "id": {
                "type": "integer"
            },
            "name": {
                "type": "string"
            },
            "categoryId": {
                "type": "string"
            },
            "price": {
                "type": "number"
            },
            "overview": {
                "type": "string"
            },
            "numberInStock": {
                "type": "integer"
            },
            "imageUrl": {
                "type": "string"
            },
        }
    }
    

    Analise o objeto BoilerPart.

    Esta ação analisa a mensagem de resposta retornada pela solicitação getBoilerParts/{id}. A resposta contém os detalhes da peça da caldeira, incluindo o número em estoque atualmente.

  18. Na caixa Escolher uma operação da nova etapa, selecione o conector HTTP.

  19. Na guia Ações, selecione a ação HTTP.

  20. Usando o menu de reticências para a operação, renomeie a operação como CheckReservations.

  21. Defina as seguintes propriedades para esta operação e selecione + Nova etapa:

    • Método: GET
    • URI: https://<APIM name>.azure-api.net/api/boilerparts/. Como antes, na caixa Conteúdo dinâmico desta URI, na guia Conteúdo dinâmico, selecione boilerPartId. No campo URI, acrescente o texto /reserved depois do espaço reservado boilerPartId

    A etapa CheckReservations.

  22. Na caixa Escolher uma operação da nova ação, na caixa Conectores de pesquisa e ações, insira Analisar JSON e, em seguida, selecione a ação Analisar JSON.

  23. Renomeie a operação como ParseReservations.

  24. Defina a propriedade Conteúdo como Corpo.

  25. Insira o esquema a seguir e selecione + Nova etapa.

    {
        "type": "object",
        "properties": {
            "id": {
                    "type": "integer"
            },
            "totalReservations": {
                    "type": "integer"
            }
        }
    }
    

    Analise os dados de reservas.

  26. Na caixa Escolher uma operação da nova ação, na caixa Conectores de pesquisa e ações, insira Condição e, em seguida, selecione a ação Controle de Condição.

    Selecione o controle de condição.

  27. Renomeie a operação como CompareStock.

  28. Selecione a caixa Escolher um valor. Na caixa Adicionar conteúdo dinâmico, na guia Expressão, insira a seguinte expressão e selecione OK.

    add(body('ParseReservations')?['totalReservations'], triggerBody()?['numberToReserve'])
    

    Esta expressão calcula a soma do número de itens da peça especificada da caldeira que estão reservados atualmente e o número solicitado pelo engenheiro.

    A condição CompareStock.

  29. Na caixa de lista suspensa de condições, selecione é maior que.

  30. Na caixa Escolher um valor restante, na caixa Conteúdo dinâmico, na guia Conteúdo dinâmico, em ParseBoilerPart, selecione numberInStock.

    Compare o total de reservas com o número de itens em estoque.

  31. Se o número de itens necessários mais o número de reservados for maior do que o número em estoque, o aplicativo precisará fazer um pedido para reabastecer o inventário. No branch Verdadeiro da ação CompareStock, selecione Adicionar uma ação.

  32. Na guia Tudo da nova operação, selecione HTTP e, em seguida, selecione a ação HTTP.

  33. Renomeie a operação como PostOrder.

  34. Configure as seguintes propriedades para a operação PostOrder:

    • Método: POST
    • URI: https://<APIM name>.azure-api.net/api/orders
    • Na tabela Consultas, na primeira linha, insira a chave boilerPartId. Para o valor na caixa Adicionar conteúdo dinâmico, na guia Conteúdo dinâmico, selecione boilerPartId
    • Na segunda linha da tabela Consultas, insira a chave quantity. No campo de valor, insira 50.

    Publique uma solicitação para encomendar mais peças.

    O aplicativo lógico solicitará automaticamente 50 itens da peça especificada quando o estoque estiver acabando.

    Observação

    O aplicativo lógico assume que o engenheiro não tentará realmente reservar mais de 50 itens de uma peça especificada em uma única solicitação.

  35. Deixe vazio o branch Falso da ação CompareStock.

  36. Abaixo da ação CompareStock, selecione + Nova etapa.

  37. Na guia Tudo da nova operação, selecione HTTP e, em seguida, selecione a ação HTTP.

  38. Renomeie a operação como PostReservation.

  39. Configure as seguintes propriedades para a operação PostReservation:

    • Método: POST
    • URI: https://<APIM name>.azure-api.net/api/reservations
    • Na tabela Consultas, na primeira linha, insira a chave boilerPartId. Para o valor na caixa Adicionar conteúdo dinâmico, na guia Conteúdo dinâmico, selecione boilerPartId.
    • Na segunda linha, insira a chave engineerId. Para o valor na caixa Adicionar conteúdo dinâmico, na guia Conteúdo dinâmico, selecione engineerId
    • Na terceira linha, insira a chave quantityToReserve. Para o valor na caixa Adicionar conteúdo dinâmico, na guia Conteúdo dinâmico, selecione numberToReserve
  40. Selecione + Nova Etapa. Na caixa Escolher uma operação, pesquise e selecione a ação Resposta.

  41. Defina as seguintes propriedades para a ação Resposta:

    • Código de Status: 200
    • Cabeçalhos: Chave: content-type, Valor: application/json
    • Corpo: na caixa Conteúdo dinâmico, selecione o elemento Corpo da solicitação PostReservation. Este é o corpo retornado no momento da reserva.

    Mensagem de resposta enviada pelo aplicativo lógico.

  42. No canto superior esquerdo da página Designer de Aplicativos Lógicos, selecione Salvar. Verifique se o aplicativo lógico pode ser salvo sem erros.

Para criar o conector personalizado que o Power Apps pode usar para acionar o aplicativo lógico, Kiana executa as seguintes etapas enquanto ainda está no portal do Azure:

  1. Na página Visão geral do aplicativo lógico, selecione Exportar.

    Exporte o aplicativo lógico.

  2. No painel Exportar para o Power Apps, nomeie o conector PartsOrderingConnector, selecione o ambiente do Power Apps e, em seguida, selecione OK.

    Exporte o aplicativo lógico para o Power Apps.

  3. Entre no Power Apps.

  4. No ambiente, em Dados, selecione Conectores Personalizados e verifique se PartsOrderingConnector está listado.

    Conectores personalizados do Power Apps.

Maria agora pode modificar o aplicativo VanArsdel para permitir que um técnico encomende peças enquanto faz o atendimento no local de um cliente. Maria adiciona um botão Pedido à tela PartDetails, como a seguir:

  1. Entre no Power Apps (se ainda não estiver conectado).

  2. Em Aplicativos, selecione o aplicativo VanArsdelApp. No menu de reticências do aplicativo, selecione Editar.

  3. No painel Dados, selecione Adicionar dados, procure o conector PartsOrderingConnector e adicione uma nova conexão usando esse conector.

    Adicione o conector PartsOrdering ao aplicativo.

  4. No painel Exibição de árvore, expanda a tela PartDetails e, em seguida, expanda o formulário DetailForm1.

  5. No painel Propriedades à direita, selecione Editar campos. No painel Campos, no menu de reticências, selecione Adicionar um cartão personalizado.

    Adicione um controle de cartão de dados personalizado ao aplicativo.

  6. No painel Exibição de árvore, renomeie o novo cartão de DataCard1 para ReserveCard. Na janela Exibição de design, redimensione o cartão para que ocupe a parte inferior da tela, abaixo do controle Image_DataCard1.

    Renomeie e redimensione o controle do cartão de dados.

  7. No menu Inserir, no submenu Inserir, adicione um controle Entrada de texto, um controle Botão e um controle Rótulo ao controle ReserveCard.

  8. Redimensione e posicione os controles para que fiquem adjacentes, com o controle Botão à direita do controle Entrada de Texto e o Rótulo abaixo do controle Botão.

  9. No painel Propriedades do controle Entrada de Texto, desmarque a propriedade Padrão.

  10. No painel Propriedades do controle Botão, defina a propriedade Texto como Reservar.

    O layout da tela ParttDetails.

  11. Renomeie o controle Entrada de Texto como NumberToReserve, renomeie o controle Botão como Reserve e renomeie o controle Rótulo como Mensagem.

  12. No painel Propriedades do controle Mensagem, defina a propriedade Texto como Peças Reservadas e defina a propriedade Visível como MessageIsVisible.

    Observação

    MessageIsVisible é uma variável que você inicializará como falso quando a tela for exibida, mas será alterada para verdadeiro se o usuário selecionar o botão Reservar.

  13. Defina a propriedade OnSelect para o controle do botão Reservar com a fórmula a seguir.

    FieldEngineerPartsOrdering.manualinvoke({boilerPartId:ThisItem.id, engineerId:"ab9f4790-05f2-4cc3-9f01-8dfa7d848179", numberToReserve:NumberToReserve.Text});
    
    Set(MessageIsVisible, true);
    

    Observação

    Esta fórmula usa uma ID de engenheiro embutida em código para representar o técnico que está executando o aplicativo no momento. O Capítulo 8 descreve como recuperar a ID do usuário conectado.

    Além disso, o aplicativo não executa nenhuma verificação de erros; ele pressupõe que a solicitação de reserva de peças sempre é bem-sucedida. Para obter mais informações sobre tratamento de erros, acesse Função Erros no Power Apps.

  14. Defina a propriedade OnVisible para a tela PartDetails como Set(MessageIsVisible, false).

Para testar o aplicativo, faça o seguinte:

  1. No painel Exibição de árvore, selecione a tela Página Inicial.

  2. Selecione F5 para visualizar o aplicativo.

  3. Na tela Página Inicial, selecione Peças.

  4. Na tela de navegação, selecione qualquer peça.

  5. Na tela Detalhes da Peça, role para baixo até a seção de reservas, insira um valor inteiro positivo e selecione Reservar. Verifique se a mensagem Peças reservadas aparece.

    A tela PartDetails com a função Reservar habilitada.

  6. Feche a janela de visualização e retorne ao Power Apps Studio.

  7. No portal do Azure, vá para a página do Banco de Dados SQL InventoryDB.

  8. Selecione o Editor de Consultas e faça login como sqladmin com sua senha.

  9. No painel Consulta 1, insira a consulta a seguir e selecione Executar. Verifique se a reserva que você fez no aplicativo VanArsdel é exibida.

    SELECT * FROM [dbo].[Reservations]
    

    A consulta resulta no Banco de Dados SQL.