← Back to list

App PO UI no Protheus e Navegador

Se você quer criar um app Angular que rode tanto no Protheus quanto no navegador, este guia passo a passo vai te ajudar a configurar tudo…

Welerson Ferreira de Araujo · 2025-01-26 19:04 · 0 claps · 8.1 min read
#po-ui #protheus #advpl #totvs #angular
Open on Medium ↗
Wiki topics: 🌐 · Web Development

App PO UI no Protheus e Navegador

Se você quer criar um app Angular que rode tanto no Protheus quanto no navegador, este guia passo a passo vai te ajudar a configurar tudo, desde a instalação até a integração completa. Vamos lá!

Link para acesso do repositório no GitHub contendo o código que iremos construir: welersonfrr/po-project

Configuração do Angular

Instalação do Angular CLI e PO-UI

Para começar, precisamos configurar o ambiente Angular e instalar as bibliotecas essenciais para que o projeto funcione corretamente com o Protheus. Vamos criar um novo projeto e preparar tudo para que possamos evoluir nas próximas etapas.

npx -p @angular/cli@17
ng new po-project --skip-install

Escolha as opções padrão:

  • Stylesheet: CSS
  • SSR e SSG: Não

Entrando na pasta do projeto

cd po-project

Instalando as dependências

npm i @totvs/protheus-lib-core@17
ng add @po-ui/ng-components@17 # Escolha SideMenu = Yes
ng add @po-ui/ng-templates

Abrindo o projeto no VSCode

code .

Criando e ajustando arquivos

Agora que temos o projeto configurado, é hora de criar e ajustar alguns arquivos que garantirão a comunicação entre o Angular e o Protheus. Essas configurações vão desde o tratamento de erros até a recepção de parâmetros do Protheus.

Corrigindo erro do provider window

Arquivo: src/app/app.config.ts

providers: [
 … // Demais providers
 { provide: "Window", useValue: window },
]

Detectando a origem da execução

Arquivo: src/app/app.component.ts

import { ProtheusLibCoreModule, ProAppConfigService } from '@totvs/protheus-lib-core';

imports: [
  ProtheusLibCoreModule,
  ... // Demais imports
]

constructor(private proAppConfigService: ProAppConfigService) {
  if (this.proAppConfigService.insideProtheus()) {
    // Executando no Protheus
  } else {
    // Executando via Browser
  }
}

Criando arquivo para receber parâmetros do Protheus

Arquivo: src/assets/preload/advpltojs.js

(codeType, content) => {
  console.log(`Preload - codeType: ${codeType} - content: ${content}`);

  if (codeType == 'filiaisComAcesso') {
    sessionStorage.setItem('FILIAIS', content);
  }
  if (codeType == 'currentFilial') {
    sessionStorage.setItem('currentFilial', content);
  }
  if (codeType == 'currentEmpresa') {
    sessionStorage.setItem('currentEmpresa', content);
  }
}

Configuração do ambiente

A configuração do ambiente é essencial para garantir que nosso app funcione corretamente tanto em desenvolvimento quanto em produção. Vamos definir as variáveis de ambiente necessárias para cada caso.

Desenvolvimento

Arquivo: src/environments/environment.ts

export const environment = {
  ambiente: "browser",
  url: "URL_REST_DO_SEU_AMBIENTE",
  path: {
    login: "/api/oauth2/v1/token/",
    defaults: "/api/protheus/session/v1/defaults"
  },
}

Produção

Arquivo: src/environments/environment.producao.ts

export const environment = {
  ambiente: "protheus",
  url: "URL_REST_DO_SEU_AMBIENTE",
  path: {
    login: "/api/oauth2/v1/token/",
    defaults: "/api/protheus/session/v1/defaults"
  },
}

Ajustes no angular.json para o Build

Substituir em projects > po-project > architect > build > options:

"outputPath": "dist/po-project"

Por:

"outputPath": {
  "base": "dist/po-project",
  "browser": ""
},

Adicionar em projects > po-project > architect > build > configurations:

"protheus": {
  "optimization": false,
  "extractLicenses": false,
  "sourceMap": true,
  "fileReplacements": [
    {
      "replace": "src/environments/environment.ts",
      "with": "src/environments/environment.producao.ts"
    }
  ]
}

Com isso, ao rodar ng serve, o app deve estar acessível no navegador.

Configuração do login

Para permitir o acesso seguro ao sistema, precisamos configurar um fluxo de autenticação. Aqui, como exemplo, iremos criar os componentes de login e home, configurar as rotas e garantir que a autenticação funcione corretamente.

Utilizaremos o template PoPageLogin: (https://po-ui.io/documentation/po-page-login)

Criando os componentes

ng g c pages/home
ng g c pages/login

Configurando as rotas

Editaremos nossas rotas para apontar para cada elemento:

Arquivo: src/app/app.routes.ts

export const routes: Routes = [
  { path: "", component: HomeComponent },
  { path: "index.html", redirectTo: "" },
  { path: "login", component: LoginComponent }
];

Alteramos o conteúdo da linha 7 do app.component.html

Aqui é onde nossas rotas serão construídas preservando tudo que está ao redor, como nome da aplicação e sidemenu

Arquivo: src/app/app.component.html

Substituir a linha contendo:

<p> AppName page </p>

Por:

<router-outlet></router-outlet>

Nesse momento, ao salvar, veremos no nosso navegador o conteúdo presente em home.component.html

No arquivo src/app/pages/login.component.html, adicione a chamada do template:

<po-page-login/>
  • Lembrando de inserir no login.component.ts o import “PoPageLoginModule”

Se navegarmos na nossa aplicação até a rota “/login” veremos que se encontra de forma estranha

Isso se dá por conta do router-outlet, corrigiremos isso agora:

No arquivo src/app/app.component.ts faça as seguintes alterações:

Declarar o router no constuctor:

constructor(
  private router: Router,
  ... // Demais variaveis
 )

Declarar a variavel: excludedRoutes:

excludedRoutes = ['/login']; // Adicione rotas que não devem exibir os wrappers

E finalmente a função:

isExcludedRoute(): boolean {
  return this.excludedRoutes.includes(this.router.url);
}

No arquivo src/app/app.component.htmlfaremos as seguintes alteraçoes:

Substituir

<div class="po-wrapper">

Por

<div *ngIf="!isExcludedRoute(); else noWrapper" class="po-wrapper">

Adicionar após o fechamento da ultima <div>

<ng-template #noWrapper>
  <router-outlet></router-outlet>
</ng-template>

Com isso nossa rota login ficará fora dos encapsulamentos e será propriamente a página

Tratamento para realização do login via navegador

Nos arquivos de login faremos as seguintes alteraçoes:

Arquivo: src/app/pages/login.component.ts

Declaração e função:

loginSubmit(data: any) {
  console.log(data);
}

Arquivo: src/app/pages/login.component.html

Inclusão da chamada dentro da tag:

<po-page-login
   (p-login-submit)="loginSubmit($event)"
/>

Com isso ao preenchermos os campos de login e senha e clicar no botao entrar, veremos nosso usuario e senha no console do navegador

Configuração do service de login

Declararemos agora o sevice responsável por realizar a solicitação do Token de autenticação para o protheus

ng g s services/login/login

Alteraremos nosso então criado LoginService da seguinte forma

Arquivo: src/app/services/login/login.service.ts

No constructor iremos adicionar junto aos seus respectivos imports:

constructor (
  public httpClient: HttpClient,
  private router: Router,
  private notification: PoNotificationService
 )

Declararemos também as seguintes funções junto aos seus Imports:

submitLogin(data: any) {
  const uri = environment.url + environment.path.login
  const params: HttpParams = new HttpParams()
    .set('grant_type','password')
    .set('username', data.login)
    .set('password', data.password)

  this.httpClient.post(uri, null, { params }).subscribe(
    {
       next:
       (res)=>{
         this.updateToken(res)
       },
       error:
       (error: any) => {
         this.notification.warning('Usuário ou senha incorretos! por favor verifique.')
       },
       complete:
       () => {
         this.notification.success('Login realizado com sucesso!')
         this.router.navigate(["/"])
       }
    }
  )
  return
 }

 updateToken(data: any) {
  sessionStorage.setItem('ERPTOKEN', JSON.stringify(data))
 }

 getAccessToken() {
  const erpToken = sessionStorage.getItem('ERPTOKEN')
  if (erpToken == null) {
    this.notification.warning('Por favor realize o login!')
    this.router.navigate(["/login"])
  } else {
    return JSON.parse(erpToken).access_token
  }
}

Agora retornaremos ao nosso login.component.ts para algumas alterações:

Arquivo: src/app/pages/login/login.component.ts

Declaramos nosso constructor com seu respectivo import:

constructor (
  private loginService: LoginService
 )

Adicionamos na nossa função loginSubmit a seguinte chamada:

loginSubmit(data: any) {
  console.log(data);
  this.loginService.submitLogin(data)
}

Realizadas as alterações, ao retornar para o navegador na rota “/login” você pode testar fazer login com um usuario incorreto e outro correto, você deverá ver os seguintes casos:

Usuario ou senha incorretos

Usuario ou senha incorretos

Login realizado corretamente — navega até a home

Login realizado corretamente — navega até a home

Outra novidade é que ao abrir o devtools, navegando até a aba Aplicativo, no Armazenamento de sessão, estará presente o ERPTOKEN

Validação de usuario logado

Agora iremos validar se o nosso usuario está logado!

Retornaremos ao arquivo app.component

Arquivo: src/app/app.component.ts

Iremos incluir no constructor o Login Service:

private loginService: LoginService

Colocaremos dentro do if:

// Executando no protheus
   this.proAppConfigService.loadAppConfig();
   this.router.navigate(['/'])

E no else:

// Executando via Browse
  this.loginService.getAccessToken()

Assim ao acessar o app via protheus o loadAppConfig() ficará responsável pelo preenchimento do ERPTOKEN nativamente, enquanto que o acesso por navegador será redirecionado para o login.

Agora prepararemos para chamada de dados de uma API para exemplo de uso do access_token.

Preparação para uso de api

Criamos um interceptor para carregar a autenticação no header das requisiçoes rest, evitando trabalho de carrega-la a cada requisição:

ng g s services/interceptor/interceptor

Vamos fazer as alteraçoes no entao criado InterceptorService

Arquivo: src/app/services/interceptor/interceptor.service.ts

  • Faça as importações conforme necessário:

Substituir:

export class InterceptorService {

Por:

export class InterceptorService implements HttpInterceptor {

Adicionar no constructor:

private loginService: LoginService

Declarar a função:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  const access_token = this.loginService.getAccessToken()
  const clone_request = req.clone({
    setHeaders: {
     Authorization: `Bearer ${access_token}`
    }
  })
  return next.handle(clone_request)
}

Alteraremos agora o arquivo app.config.ts para que nosso interceptor atue:

Arquivo: src/app/app.config.ts

Pequena alteração no provider:

providers: [
  ... // Demais providers
  provideHttpClient(
    withInterceptorsFromDi() // Incluir dentro dos parenteses
  ),
  {provide: HTTP_INTERCEPTORS, useClass: InterceptorService, multi: true}, // Incluir esta linha
  ... // Demais providers
 ]

Com isso qualquer requisição http realizada no escopo da aplicação irá carregar consigo o access_token no Header.

Vamos fazer nossa requisição

Criamos mais um service que ficará responsável pela chamada da api:

ng g s services/api/api

No novo ApiService criado vamos fazer as alterações, realizando os imports necessários.

Arquivo: src/app/services/api/api.service.ts

Incluimos no constructor:

private httpClient: HttpClient

Declaramos a função:

getData() {
  const url = environment.url + environment.path.defaults
  return this.httpClient.get(url)
}

No HomeComponent faremos as alterações juntamente com os imports necessários:

Arquivo: src/app/pages/home/home.component.ts

Importamos o Po Module para uso dos componente da PO-UI

imports: [
  PoModule
 ],

Declaramos uma variável data

data: string = ''

Declaramos o ApiService no constructor

private apiService: ApiService

Declaramos a função getData() responsável por capturar os valores:

getData() {
  this.apiService.getData().subscribe({
    next:
    (req: any) => {
     console.log(req); // Verifique no console o retorno completo da api
     this.data = req.base_date // O retorno base_date será impresso na tela
    },
    error:
    (err: any) => { },
    complete:
    () => { }
  })
 }

Arquivo: src/app/pages/home/home.component.html

Aqui faremos o disparo da chamada da função e impressão na tela

<p>{{data}}</p>
<po-button (p-click)="getData()"  p-label="Button"/>

Com isso nosso app esta totalmente preparado para uso pelo navegador!!

Preparação para uso no Protheus

Agora que nosso app está pronto para ser utilizado no navegador, é hora de prepará-lo para rodar dentro do Protheus. Isso envolve a geração dos arquivos necessários e a integração com a plataforma.

Build do projeto

ng build -c protheus

Após a compilação, uma pasta chamada dist será gerada na raiz do projeto. Dentro dela, haverá uma subpasta com o nome da aplicação definida no output-path. Esta pasta deverá ser preparada para uso no Protheus.

Preparação dos arquivos

  1. Compacte a pasta em formato .zip: po-project.zip
  2. Renomeie a extensão para .app: po-project.app
  3. Compile esse arquivo .app no ambiente Protheus (Se estiver utilizando o VS Code, pode ser necessário incluir a extensão .app na lista de extensões permitidas para compilação).

Criando a função ADVPL

Agora vamos criar uma função ADVPL para chamar a aplicação Angular dentro do Protheus.

Arquivo: TEST001.prw

#include "protheus.ch"

User Function TEST001()
  FwCallApp("po-project")
Return 

Static Function JsToAdvpl(oWebChannel, cType, cContent)
  Local cFCAcss := ''
  Local i

  if (FWUsrEmp(__cUserID)[1] == '@@@@')
    for i := 1 to Len(LoadFils())
      cFCAcss += LoadFils()[i] + '-'
    next
  else
    for i := 1 to Len(FWUsrEmp(__cUserID))
      cFCAcss += SubString(FWUsrEmp(__cUserID)[i], 3, 6) + '-'
    next
  endif

  oWebChannel:AdvPLToJS('filiaisComAcesso', cFCAcss)
  oWebChannel:AdvPLToJS('currentFilial', cFilAnt)
  oWebChannel:AdvPLToJS('currentEmpresa', cEmpAnt)
Return .T.

Essa função permite chamar o app Angular pelo menu do Protheus e transferir dados entre o Protheus e a aplicação Angular.

O responsável pela chamada é apenas o FwCallApp, a Static Function JsToAdvplé responsável pela comunicação com o arquivo AdvplToJs presente no app angular.

Neste projeto o AdvplToJsnão foi utilizado mas já esta pronto para uso conforme necessidade.

Acesso via navegador

Com a aplicação configurada, podemos acessá-la via navegador utilizando a seguinte URL:

http://<IP_SERVIDOR_PROTHEUS>:<PORTA>/app-root/po-project/

Por exemplo:

http://127.0.0.1:1234/app-root/po-project/

Considerações finais

Certifique-se de que o servidor Protheus está corretamente configurado para servir aplicações web e que as permissões estão ajustadas conforme necessário.

Agora que finalizamos todas as configurações, temos um app Angular funcional que pode ser acessado tanto via navegador quanto pelo Protheus. Esse tipo de integração permite criar aplicações modernas e responsivas aproveitando o poder do Angular enquanto mantém a compatibilidade com os processos do ERP Protheus.

Se tiver dúvidas ou problemas durante a implementação, revise cada etapa cuidadosamente e consulte a documentação oficial do PO-UI e do Protheus para obter mais detalhes.

Espero que este guia tenha sido útil para você. Boa codificação!


메타데이터
post_id
7bb725faad76
slug
app-po-ui-no-protheus-e-navegador-7bb725faad76
url
https://medium.com/@contato.welerson/app-po-ui-no-protheus-e-navegador-7bb725faad76
canonical_url
https://medium.com/@contato.welerson/app-po-ui-no-protheus-e-navegador-7bb725faad76
author_url
https://medium.com/@contato.welerson
status
ok
fetched_at
2026-06-28 10:39:35