Temos como objetivo, implementar técnicas para facilitar a personalização de telas TOTVS - Linha Datasul de forma lowcode, apenas com cadastro de campos por parte do cliente.
A partir da release 12.1.31, são disponibilizados as técnicas e cadastros para implementar a personalização em telas HTML da linha Datasul.
Nesta técnica de personalização, o desenvolvedor deverá realizar o cadastro dos campos a serem personalizados e criar alguns componentes em PO-UI que utilizem os componentes: PO-DYNAMIC-FORM, PO-DYNAMIC-VIEW e PO-PAGE-DYNAMIC-TABLE (este último somente se for necessário). Deve-se também implementar endpoint em Progress no qual será utilizado como fonte de dados para os campos personalizados.
Para utilização desta técnica será necessário ter um conhecimento de desenvolvimento com: APIs REST em Progress, Angular, TypeScript e PO-UI.
A Técnica de personalização de telas HTML com PO-UI contempla os seguintes objetos:
Retorna a lista de campos personalizados, que devem ser previamente cadastrados na tela de Personalização em HTML.
Neste item, deverá ser utilizado o endpoint Progress /api/btb/v1/personalizationView/metadata/ + código_do_programa ,onde deve ser passado o Código do Programa Datasul que conterá a lista de campos personalizados.
Exemplo: Implementação de personalização para o programa pedido-execucao-monitor, deve-se utilizar o endpoint "/api/btb/v1/personalizationView/metadata/pedido-execucao-monitor"
{ "fields": [ { "visible": true, "gridColumns": 6, "disable": false, "property": "cod_idioma", "label": "Idioma", "type": "string" }, { "visible": true, "gridColumns": 6, "disable": false, "property": "cod_idiom_padr", "label": "Idioma Padrão", "type": "string" }, { "visible": true, "gridColumns": 6, "disable": false, "property": "des_idioma", "label": "Descrição", "type": "string" }, { "visible": false, "property": "id", "type": "number", "key": true } ] } |
Deve retornar os dados que serão apresentados nos campos personalizados;
A tabela a seguir demonstra os componentes dinâmicos do PO-UI com suas respectivas requisições à API REST
Componente PO-UI | Endpoint | Tipo Requisição | Descrição |
---|---|---|---|
po-dynamic-view po-dynamic-form | /byid/nome_do_programa/id | GET | Retorna um registro único. Recebe no PathParms o "nome do programa" e o "id". |
po-page-dynamic-table | /nome_do_programa | GET | Retorna uma lista de registros. Recebe no PathParams o "nome do programa". |
po-dynamic-form | /validateForm/nome_do_programa | POST | Valida o formulário. Recebe no PathParams o "nome do programa". |
po-dynamic-form | /nome_do_programa | POST | Efetua a criação de um novo registro. Recebe no PathParams o "nome do programa". |
po-dynamic-form | /nome_do_programa/id | PUT | Efetua a alteração do registro, Recebe no PathParams o "nome do programa" e o "id". |
po-dynamic-form | /nome_do_programa/id | DELETE | Efetua a eliminação do registro. Recebe no PathParams o "nome do programa" e o "id". |
Em todas os componentes dinâmicos da tabela apresentada, efetuará uma requisição para obter a lista de campos personalizados na API REST do Framework, com a utilização da requisição "/api/btb/v1/personalizationView/metadata/" + código_do_programa |
Foi implementado no Progress um utilitário btb/personalizationUtil.p, com seu include btb/personalizationUtil.i, que deve ser utilizado para retornar à área de negócio a lista de campos personalizáveis de um determinado programa cujo o intuito é facilitar a implementação para que seja enviado somente os valores dos campos personalizáveis.
Devido a característica do PO-UI dinâmico, caso seja enviado os dados de campos que não estão na lista de campos, o PO-UI irá apresentar o valor do campo com uma label com o mesmo nome do campo. Com a obtenção da lista de campos, pode-se evitar o envio de informações que estão fora da lista de campos personalizados.
DEFINE TEMP-TABLE ttPersonalization NO-UNDO FIELD codProgDtsul AS CHARACTER FIELD codField AS CHARACTER FIELD codType AS CHARACTER FIELD codLabel AS CHARACTER FIELD codValid AS CHARACTER FIELD logReadOnly AS LOGICAL INITIAL FALSE FIELD logEnable AS LOGICAL INITIAL TRUE INDEX codigo IS PRIMARY codProgDtsul codField. |
Procedure | Parâmetros | Descrição/Exemplo | |
---|---|---|---|
piGetTTPersonalizaton | INPUT cProg AS CHARACTER OUTPUT TABLE ttPersonalization | Retorna a temp-table ttPersonalization com a lista de campos personalizáveis de um determinado programa. Exemplo:
| |
piGetFieldList | INPUT cProg AS CHARACTER OUTPUT cList AS CHARACTER OUTPUT cTypeList AS CHARACTER | Retorna duas listas CHARACTER, uma contendo a lista de campos e uma lista dos seus respectivos tipos, de um determinado programa. Observação: As listas usam como separador a vírgula ",". Exemplo:
|
Todos os componentes dinâmicos do PO-UI realizam, no mínimo, duas requisições REST, uma para obter a lista de campos personalizáveis e outra para obter os dados a serem apresentados nesses campos.
Abaixo temos o papel de cada componente dinâmico que podemos utilizar:
Pode-se implementar também um componente PO-PAGE-DYNAMIC-TABLE, para a navegação dos registros e permitir a visualização e edição dos mesmos. |
Em nossa técnica, em todas as requisições REST, será enviado:
A seguir são apresentados as telas necessárias para a realização do cadastro dos campos personalizados.
Ao localizar no menu o programa "Campos personalizados (html.personalization-metadata)", é apresentada uma tela em formato de 'lista' que conterá todos os campos (metadados) cadastrados no produto Datasul. Para cadastrar um campo que será utilizado na personalização, basta clicar no botão +Adicionar.
A tela a seguir apresenta o cadastro do 'metadado' relacionado a um campo que pode ser apresentado no programa como personalizado.
Campo | Descrição | Obrigatório | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Código Programa Datasul | Código do programa "base" que podem ser aplicadas as técnicas de personalização
| Sim | ||||||||||||||||||
Identificador Campo | Identificador único do campo (por programa), necessário para a geração da tela personalizada (código do campo) | Sim | ||||||||||||||||||
Nome Campo | Nome do campo que será apresentado na tela (label do campo) Caso o campo não seja informado, o nome do campo apresentado será o informado no identificador. | Não | ||||||||||||||||||
Tipo Campo | Tipo do campo cadastrado Caso o campo não seja informado, será considerado que o campo é do tipo Character. Tipos de campos permitidos:
| Não | ||||||||||||||||||
Somente Leitura | Opção para que o campo seja apresentado como 'somente leitura' (torna o campo readOnly) | Sim | ||||||||||||||||||
Habilita personalização | Opção para habilitar ou desabilitar a apresentação da personalização por campo (torna o campo visível) | Sim |
Após cadastrar o campo, o mesmo é apresentado na tela inicial onde pode ser realizado filtros sobre seus resultados, bem como efetuar ações de edição de campos, inclusão de atributos e exclusão de campos.
Ao clicar na opção de editar, não será possível modificar o código do programa Datasul vinculado e também seu identificador. Os demais campos estão habilitados para edição.
Com a configuração de atributos dos campo personalizado, é possível adicionar outras características tais como:
Ao clicar no botão "Atributos" apresentado na grid principal (Opções), uma tela de parametrizações é apresentada para que seja possível adicionar as novas características.
Ao renderizar os campos personalizados em tela, esses atributos serão inclusos no campo personalizado e enviados para tela.
Os nomes dos atributos devem ser os mesmos que estão documentados nas propriedades do componente no PO-UI. Exemplo: Para personalizar um campo no formato CPF, criamos o campo COD_CPF e adicionamos um atributo do tipo "mask" que conterá o formato "999.999.999-99". O atributo "mask" corresponde a uma propriedade do componente PO-INPUT. |
A seguir, são apresentados exemplos de códigos para a implementação com HTML e TypeScript:
<po-loading-overlay [hidden]="!showLoading"> </po-loading-overlay> <po-page-detail p-title="Detalhe do Idioma" [p-breadcrumb]="breadcrumb" (p-edit)="editClick()" (p-back)="goBackClick()"> <po-dynamic-view [p-fields]="fields" [p-value]="record"> </po-dynamic-view> </po-page-detail |
import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { PoBreadcrumb, PoBreadcrumbItem } from '@po-ui/ng-components'; import { PersonalizationService } from '../personalization.service'; @Component({ selector: 'app-personalization-detail', templateUrl: './personalization-detail.component.html', styleUrls: ['./personalization-detail.component.css'] }) export class PersonalizationDetailComponent implements OnInit { public static cProg = 'html.aplicativos-eai'; // definicao das variaveis utilizadas public currentId: string; public fields: Array<any> = []; public record = {}; public showLoading = false; public breadcrumb: PoBreadcrumb = { items: [] }; public breadcrumbItem: PoBreadcrumbItem; // construtor com os servicos necessarios constructor( private service: PersonalizationService, private activatedRoute: ActivatedRoute, private route: Router ) { } // load do componente public ngOnInit(): void { this.activatedRoute.params.subscribe(pars => { this.showLoading = true; this.record = {}; // Carrega o registro pelo ID // tslint:disable-next-line:no-string-literal this.currentId = pars['id']; // busca os valores dos dados a serem apresentados this.service.loadValuesById(this.currentId).subscribe(resp => { Object.keys(resp).forEach((key) => this.record[key] = resp[key]); // carrega a lista de campos somente apos receber os dados a serem apresentados this.service.loadMetadata().subscribe(metadata => { // tslint:disable-next-line:no-string-literal this.fields = metadata['fields']; this.showLoading = false; }); }); }); this.setBreadcrumb(); } private setBreadcrumb(): void { this.breadcrumbItem = { label: 'Home', link: '/' }; this.breadcrumb.items = this.breadcrumb.items.concat(this.breadcrumbItem); this.breadcrumbItem = { label: 'Listagem de Idiomas' , link: '/personalization' }; this.breadcrumb.items = this.breadcrumb.items.concat(this.breadcrumbItem); this.breadcrumbItem = { label: 'Detalhe do Idioma' }; this.breadcrumb.items = this.breadcrumb.items.concat(this.breadcrumbItem); } // Redireciona quando clicar no botao Edit public editClick(): void { this.route.navigate(['/personalization', 'edit', this.currentId]); } // Redireciona quando clicar no botao Voltar public goBackClick(): void { this.route.navigate(['/personalization']); } } |
<po-loading-overlay [hidden]="!showLoading"> </po-loading-overlay> <po-page-edit [p-title]="cTitle" [p-breadcrumb]="breadcrumb" [p-disable-submit]="formEdit.form.invalid" (p-cancel)="cancelClick()" (p-save)="saveClick()"> <po-dynamic-form #formEdit p-auto-focus="string" [p-fields]="fields" [p-validate]="validationUrl" [p-value]="record"> </po-dynamic-form> </po-page-edit> |
import { Component, OnInit, ViewChild } from '@angular/core'; import { NgForm } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { PoBreadcrumb, PoBreadcrumbItem, PoDialogService, PoNotificationService } from '@po-ui/ng-components'; import { PersonalizationService } from './../personalization.service'; @Component({ selector: 'app-personalization-edit', templateUrl: './personalization-edit.component.html', styleUrls: ['./personalization-edit.component.css'] }) export class PersonalizationEditComponent implements OnInit { // Define as variaveis a serem utilizadas public cTitle: string; public currentId: string; public record = {}; public fields: Array<any> = []; public isUpdate = false; public showLoading = false; public validationUrl = this.service.getUrlAreaValidation(); public breadcrumb: PoBreadcrumb = { items: [] }; public breadcrumbItem: PoBreadcrumbItem; // Obtem a referencia do componente HTML @ViewChild('formEdit', { static: true }) formEdit: NgForm; // Construtor da classe com os servicos necessarios constructor( private service: PersonalizationService, private activatedRoute: ActivatedRoute, private route: Router, private poDialog: PoDialogService, private poNotification: PoNotificationService ) { } // Load do componente public ngOnInit(): void { this.isUpdate = false; this.showLoading = true; // Carrega o registro pelo ID this.activatedRoute.params.subscribe(pars => { // tslint:disable-next-line:no-string-literal this.currentId = pars['id']; // Se nao tiver o ID definido sera um CREATE if (this.currentId === undefined) { this.isUpdate = false; this.cTitle = 'Novo Idioma'; } else { this.isUpdate = true; this.cTitle = 'Edição do Idioma'; } // Atualiza o breadcrumb de acordo com o tipo de edicao this.setBreadcrumb(); // Se for uma alteracao, busca o registro a ser alterado if (this.isUpdate) { this.service.loadValuesById(this.currentId).subscribe(resp => { Object.keys(resp).forEach((key) => this.record[key] = resp[key]); // Em alteracao temos que receber o registro para depois buscar a lista de campos this.getMetadata(); }); } else { // Se for create, pega a lista de campos this.getMetadata(); } }); } private setBreadcrumb(): void { this.breadcrumbItem = { label: 'Home', action: this.beforeRedirect.bind(this) }; this.breadcrumb.items = this.breadcrumb.items.concat(this.breadcrumbItem); this.breadcrumbItem = { label: 'Listagem de Idiomas', action: this.beforeRedirect.bind(this) }; this.breadcrumb.items = this.breadcrumb.items.concat(this.breadcrumbItem); this.breadcrumbItem = { label: this.cTitle }; this.breadcrumb.items = this.breadcrumb.items.concat(this.breadcrumbItem); } // Retorna a lista de campos private getMetadata() { this.service.loadMetadata().subscribe(metadata => { // tslint:disable-next-line:no-string-literal this.fields = metadata['fields']; this.showLoading = false; }); } // Redireciona via breadcrumb private beforeRedirect(itemBreadcrumbLabel) { if (this.formEdit.valid) { this.route.navigate(['/']); } else { this.poDialog.confirm({ title: 'Cancelamento de edição', message: 'Os dados ainda não foram gravados, confirma redirecinamento ?', confirm: () => this.route.navigate(['/']) }); } } // Grava o registro quando clicado no botao Salvar public saveClick(): void { this.showLoading = true; if (this.isUpdate) { // Altera um registro ja existente this.service.update(this.currentId, this.record).subscribe(resp => { this.poNotification.success('Dados atualizados com sucesso'); this.showLoading = false; this.route.navigate(['/personalization']); }); } else { // Cria um registro novo this.service.create(this.currentId, this.record).subscribe(resp => { this.poNotification.success('Dados criados com sucesso'); this.showLoading = false; this.route.navigate(['/personalization']); }); } } // Cancela a edicao e redireciona ao clicar no botao Cancelar public cancelClick(): void { this.poDialog.confirm({ title: 'Confirmar cancelamento', message: 'Voce deseja realmente cancelar a edição?', confirm: () => this.route.navigate(['/personalization']) }); } } |
<po-loading-overlay [hidden]="!showLoading"> </po-loading-overlay> <po-page-dynamic-table p-auto-router p-title="Listagem de Idiomas" [p-actions]="actions" [p-breadcrumb]="breadcrumb" [p-fields]="fields" [p-service-api]="serviceApi"> </po-page-dynamic-table> |
import { Component, OnInit } from '@angular/core'; import { PoBreadcrumb, PoBreadcrumbItem } from '@po-ui/ng-components'; import { PoPageDynamicTableActions } from '@po-ui/ng-templates'; import { PersonalizationService } from './../personalization.service'; @Component({ selector: 'app-personalization-list', templateUrl: './personalization-list.component.html', styleUrls: ['./personalization-list.component.css'] }) export class PersonalizationListComponent implements OnInit { // Definicao das variaveis utilizadas public serviceApi: string; public fields: Array<any> = []; public showLoading = false; public literals; public readonly actions: PoPageDynamicTableActions = { new: '/personalization/create', detail: '/personalization/detail/:id', edit: '/personalization/edit/:id', remove: true }; public breadcrumb: PoBreadcrumb = { items: [] }; public breadcrumbItem: PoBreadcrumbItem; // Construtor da classe constructor( private service: PersonalizationService ) { } // Load do componente public ngOnInit(): void { this.fields = []; this.serviceApi = this.service.getUrlArea(); this.showLoading = true; this.service.loadMetadata().subscribe(metadata => { // tslint:disable-next-line:no-string-literal this.fields = metadata['fields']; this.showLoading = false; }); this.setBreadcrumb(); } private setBreadcrumb(): void { this.breadcrumbItem = { label: 'Home', link: '/' }; this.breadcrumb.items = this.breadcrumb.items.concat(this.breadcrumbItem); this.breadcrumbItem = { label: 'Listagem de Idiomas' }; this.breadcrumb.items = this.breadcrumb.items.concat(this.breadcrumbItem); } } |
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { PoNotificationService } from '@po-ui/ng-components'; const httpOptions: object = { headers: new HttpHeaders({ 'Content-Type': 'application/json', // 'Authorization': 'Basic ' + btoa('super:super@123'), // 'Access-Control-Allow-Origin': 'http://localhost:4200', // 'Access-Control-Allow-Headers': 'Content-Type, Access-Control-Allow-Origin, Access-Control-Allow-Headers, X-Requested-With', // 'returnFormatVersion': '2', }) }; @Injectable({ providedIn: 'root' }) export class PersonalizationService { public progCode = 'html.aplicativos-eai'; // Endpoint progress do framework para obtencao da lista de campos personalizados private urlMetadata = '/api/btb/v1/personalizationView/metadata/'; // Endpoint progress da area de negocio para obtencao dos valores dos campos personalizados private urlArea = '/api/trn/v1/idiomaValues/'; constructor( private http: HttpClient, private poNotification: PoNotificationService, ) { } public loadMetadata() { return this.http.post<any[]>(this.urlMetadata + this.progCode, httpOptions).pipe(); } public loadValuesById(cId) { // tslint:disable-next-line:whitespace return this.http.get<any[]>(this.urlArea + 'byid/' + this.progCode + '/' + cId, httpOptions).pipe(); } public loadAllValues() { return this.http.get<any[]>(this.urlArea + this.progCode + '/', httpOptions).pipe(); } public create(cId, record) { return this.http.post<any[]>(this.urlArea + this.progCode + '/' + cId, record, httpOptions).pipe(); } public update(cId, record) { return this.http.put<any[]>(this.urlArea + this.progCode + '/' + cId, record, httpOptions).pipe(); } public getUrlArea(): string { return this.urlArea + this.progCode + '/'; } public getUrlAreaValidation(): string { return this.urlArea + 'validateForm/' + this.progCode; } } |
A seguir, são apresentados exemplos de endpoint em Progress, que deverão ser implementados pela área de negócio para obtenção dos valores a serem apresentados nos campos personalizados.
{utp/ut-api.i} {utp/ut-api-action.i pGetDataById GET /byid/~* } {utp/ut-api-action.i pGetAll GET /~* } {utp/ut-api-action.i pValidateForm POST /validateForm/~* } {utp/ut-api-action.i pCreate POST /~* } {utp/ut-api-action.i pUpdate PUT /~* } {utp/ut-api-action.i pDelete DELETE /~* } {utp/ut-api-notfound.i} {btb/personalizationUtil.i} DEFINE VARIABLE oRequest AS JsonAPIRequestParser NO-UNDO. DEFINE VARIABLE oResponse AS JsonAPIResponse NO-UNDO. DEFINE VARIABLE oObj AS JsonObject NO-UNDO. DEFINE VARIABLE oList AS JsonArray NO-UNDO. DEFINE VARIABLE oBody AS JsonObject NO-UNDO. DEFINE VARIABLE cId AS CHARACTER NO-UNDO. DEFINE VARIABLE cProg AS CHARACTER NO-UNDO. DEFINE VARIABLE cList AS CHARACTER NO-UNDO. DEFINE VARIABLE cTypeList AS CHARACTER NO-UNDO. DEFINE VARIABLE cFld AS CHARACTER NO-UNDO. DEFINE VARIABLE cType AS CHARACTER NO-UNDO. DEFINE VARIABLE ix AS INTEGER NO-UNDO. DEFINE VARIABLE hTab AS HANDLE NO-UNDO. DEFINE VARIABLE hQry AS HANDLE NO-UNDO. DEFINE VARIABLE hPers AS HANDLE NO-UNDO. DEFINE VARIABLE rTab AS RECID NO-UNDO. /** Procedure que retorna os valores **/ PROCEDURE pGetDataById: DEFINE INPUT PARAMETER oJsonInput AS JsonObject NO-UNDO. DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO. oObj = NEW JsonObject(). // Le os parametros enviados pela interface HTML oRequest = NEW JsonAPIRequestParser(oJsonInput). // Obtem o programa e o codigo do registro corrente cProg = oRequest:getPathParams():getCharacter(2) NO-ERROR. cId = oRequest:getPathParams():getCharacter(3) NO-ERROR. LOG-MANAGER:WRITE-MESSAGE("pGetDataById - cProg = " + cProg, ">>>>>"). LOG-MANAGER:WRITE-MESSAGE("pGetDataById - cId = " + cId, ">>>>>"). // retorna a lista de campos a serem personalizados EMPTY TEMP-TABLE ttPersonalization. RUN btb/personalizationUtil.p PERSISTENT SET hPers. RUN piGetTTPersonalization IN hPers (cProg, OUTPUT table ttPersonalization). DELETE PROCEDURE hPers NO-ERROR. // se houver algum campo personalizado, busca as informacoes FIND FIRST ttPersonalization NO-LOCK NO-ERROR. IF AVAILABLE ttPersonalization THEN DO: FIND idioma WHERE RECID(idioma) = integer(cId) NO-LOCK NO-ERROR. IF AVAILABLE idioma THEN DO: oObj:add("id", RECID(idioma)). // deve somente alimentar os campos que serao personalizados hTab = BUFFER idioma:HANDLE. FOR EACH ttPersonalization: ASSIGN cFld = ttPersonalization.codField. oObj:add(cFld, hTab:BUFFER-FIELD(cFld):buffer-value()) NO-ERROR. END. hTab:BUFFER-RELEASE(). DELETE OBJECT hTab NO-ERROR. END. LOG-MANAGER:WRITE-MESSAGE("pGetDataById - oObj = " + String(oObj:getJsonText()), ">>>>>"). END. oResponse = NEW JsonAPIResponse(oObj). oJsonOutput = oResponse:createJsonResponse(). END PROCEDURE. /** Procedure que retorna os valores **/ PROCEDURE pGetAll: DEFINE INPUT PARAMETER oJsonInput AS JsonObject NO-UNDO. DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO. oList = NEW JsonArray(). // Le os parametros enviados pela interface HTML oRequest = NEW JsonAPIRequestParser(oJsonInput). // Obtem o programa e o codigo do registro corrente cProg = oRequest:getPathParams():getCharacter(1) NO-ERROR. LOG-MANAGER:WRITE-MESSAGE("pGetAll - cProg = " + cProg, ">>>>>"). // retorna a lista de campos a serem personalizados EMPTY TEMP-TABLE ttPersonalization. RUN btb/personalizationUtil.p PERSISTENT SET hPers. RUN piGetTTPersonalization IN hPers (cProg, OUTPUT table ttPersonalization). DELETE PROCEDURE hPers NO-ERROR. // se houver algum campo personalizado, busca as informacoes FIND FIRST ttPersonalization NO-LOCK NO-ERROR. IF AVAILABLE ttPersonalization THEN DO: FOR EACH idioma NO-LOCK: oObj = NEW JsonObject(). oObj:add("id", RECID(idioma)). // deve somente alimentar os campos que serao personalizados hTab = BUFFER idioma:HANDLE. FOR EACH ttPersonalization: ASSIGN cFld = ttPersonalization.codField. oObj:add(cFld, hTab:BUFFER-FIELD(cFld):buffer-value()) NO-ERROR. END. hTab:BUFFER-RELEASE(). DELETE OBJECT hTab NO-ERROR. oList:add(oObj). END. END. oObj = NEW JSonObject(). oObj:add("items", oList). LOG-MANAGER:WRITE-MESSAGE("pGetAll - oObj = " + String(oObj:getJsonText()), ">>>>>"). oResponse = NEW JsonAPIResponse(oObj). oJsonOutput = oResponse:createJsonResponse(). END PROCEDURE. /** Procedure que cria um novo registro na tabela **/ PROCEDURE pCreate: DEFINE INPUT PARAMETER oJsonInput AS JsonObject NO-UNDO. DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO. DEFINE VARIABLE cCodIdioma AS CHARACTER NO-UNDO. DEFINE VARIABLE cDesIdioma AS CHARACTER NO-UNDO. DEFINE VARIABLE cCodIdiomPadr AS CHARACTER NO-UNDO. DEFINE VARIABLE rIdioma AS RECID NO-UNDO. DEFINE VARIABLE lCreated AS LOGICAL NO-UNDO INITIAL FALSE. // Le os parametros e os dados enviados pela interface HTML oRequest = NEW JsonAPIRequestParser(oJsonInput). oBody = oRequest:getPayload(). rTab = ?. // Obtem o programa e o codigo do registro corrente cProg = oRequest:getPathParams():getCharacter(1) NO-ERROR. cId = oRequest:getPathParams():getCharacter(2) NO-ERROR. LOG-MANAGER:WRITE-MESSAGE("pCreate - cProg = " + cProg, ">>>>>"). LOG-MANAGER:WRITE-MESSAGE("pCreate - cId = " + cId, ">>>>>"). // retorna a lista de campos a serem personalizados EMPTY TEMP-TABLE ttPersonalization. RUN btb/personalizationUtil.p PERSISTENT SET hPers. RUN piGetTTPersonalization IN hPers (cProg, OUTPUT table ttPersonalization). DELETE PROCEDURE hPers NO-ERROR. // se houver algum campo personalizado, busca as informacoes FIND FIRST ttPersonalization NO-LOCK NO-ERROR. IF AVAILABLE ttPersonalization THEN DO TRANSACTION ON ERROR UNDO, LEAVE: CREATE idioma. // faz a gravacao dos dados onde os campos personalizados estao com o mesmo nome dos campos da tabela hTab = BUFFER idioma:HANDLE. RUN piSetPersonalizationData (hTab, oBody). rTab = hTab:RECID. hTab:BUFFER-RELEASE() NO-ERROR. DELETE OBJECT hTab NO-ERROR. END. // Retorna o ID e se foi criado com sucesso oBody = NEW JsonObject(). oBody:add('id', rTab). oBody:add('created', (IF lCreated THEN 'OK' ELSE 'NOK')). // Retorna o oBody montado para a interface HTML oResponse = NEW JsonAPIResponse(oBody). oJsonOutput = oResponse:createJsonResponse(). END PROCEDURE. /** Procedure que atualiza o conteudo do registro pelo ID **/ PROCEDURE pUpdate: DEFINE INPUT PARAMETER oJsonInput AS JsonObject NO-UNDO. DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO. DEFINE VARIABLE lUpdated AS LOGICAL NO-UNDO INITIAL FALSE. DEFINE VARIABLE oIdioma AS JsonObject NO-UNDO. DEFINE VARIABLE oId AS JsonObject NO-UNDO. // Le os parametros e os dados enviados pela interface HTML oRequest = NEW JsonAPIRequestParser(oJsonInput). oBody = oRequest:getPayload(). oObj = NEW JsonObject(). cProg = oRequest:getPathParams():getCharacter(1) NO-ERROR. cId = oRequest:getPathParams():getCharacter(2) NO-ERROR. LOG-MANAGER:WRITE-MESSAGE("pUpdate - cProg = " + cProg, ">>>>>"). LOG-MANAGER:WRITE-MESSAGE("pUpdate - cId = " + cId, ">>>>>"). // retorna a lista de campos a serem personalizados EMPTY TEMP-TABLE ttPersonalization. RUN btb/personalizationUtil.p PERSISTENT SET hPers. RUN piGetTTPersonalization IN hPers (cProg, OUTPUT table ttPersonalization). DELETE PROCEDURE hPers NO-ERROR. // se houver algum campo personalizado, busca as informacoes FIND FIRST ttPersonalization NO-LOCK NO-ERROR. IF AVAILABLE ttPersonalization THEN DO TRANSACTION ON ERROR UNDO, LEAVE: FIND idioma WHERE RECID(idioma) = integer(cId) EXCLUSIVE-LOCK NO-ERROR. IF AVAILABLE idioma THEN DO: oObj:add("id", RECID(idioma)). // faz a gravacao dos dados onde os campos personalizados estao com o mesmo nome dos campos da tabela hTab = BUFFER idioma:HANDLE. RUN piSetPersonalizationData (hTab, oBody). hTab:BUFFER-RELEASE() NO-ERROR. DELETE OBJECT hTab NO-ERROR. lUpdated = TRUE. END. END. // Retorna o ID e se foi atualizado com sucesso oBody = NEW JsonObject(). oBody:add('id', cId). oBody:add('updated', (IF lUpdated THEN 'OK' ELSE 'NOK')). // Retorna o oBody montado para a interface HTML oResponse = NEW JsonAPIResponse(oBody). oJsonOutput = oResponse:createJsonResponse(). END PROCEDURE. /** Procedure que atualiza o conteudo do registro pelo ID **/ PROCEDURE pDelete: DEFINE INPUT PARAMETER oJsonInput AS JsonObject NO-UNDO. DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO. DEFINE VARIABLE lDeleted AS LOGICAL NO-UNDO INITIAL FALSE. // Le os parametros enviados pela interface HTML oRequest = NEW JsonAPIRequestParser(oJsonInput). // Le os parametros e os dados enviados pela interface HTML oRequest = NEW JsonAPIRequestParser(oJsonInput). oBody = oRequest:getPayload(). oObj = NEW JsonObject(). cProg = oRequest:getPathParams():getCharacter(1) NO-ERROR. cId = oRequest:getPathParams():getCharacter(2) NO-ERROR. LOG-MANAGER:WRITE-MESSAGE("pDelete - cProg = " + cProg, ">>>>>"). LOG-MANAGER:WRITE-MESSAGE("pDelete - cId = " + cId, ">>>>>"). // retorna a lista de campos a serem personalizados EMPTY TEMP-TABLE ttPersonalization. RUN btb/personalizationUtil.p PERSISTENT SET hPers. RUN piGetTTPersonalization IN hPers (cProg, OUTPUT table ttPersonalization). DELETE PROCEDURE hPers NO-ERROR. // somente elimina o registro se houverem campos personalizaveis FIND FIRST ttPersonalization NO-LOCK NO-ERROR. IF AVAILABLE ttPersonalization THEN DO TRANSACTION ON ERROR UNDO, LEAVE: FIND idioma WHERE RECID(idioma) = integer(cId) EXCLUSIVE-LOCK NO-ERROR. IF AVAILABLE idioma THEN DO: DELETE idioma. ASSIGN lDeleted = TRUE. END. END. // Retorna o ID e se foi criado com sucesso oObj = NEW JsonObject(). oObj:add('id', cId). oObj:add('deleted', (IF lDeleted THEN 'OK' ELSE 'NOK')). // Retorna o oBody montado para a interface HTML oResponse = NEW JsonAPIResponse(oObj). oJsonOutput = oResponse:createJsonResponse(). END PROCEDURE. PROCEDURE pValidateForm: DEFINE INPUT PARAMETER oJsonInput AS JsonObject NO-UNDO. DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO. DEFINE VARIABLE cProp AS CHARACTER NO-UNDO. DEFINE VARIABLE oValue AS JsonObject NO-UNDO. DEFINE VARIABLE cValue AS CHARACTER NO-UNDO. DEFINE VARIABLE oNewValue AS JsonObject NO-UNDO. DEFINE VARIABLE oNewFields AS JsonArray NO-UNDO. DEFINE VARIABLE cFocus AS CHARACTER NO-UNDO. DEFINE VARIABLE oRet AS JsonObject NO-UNDO. DEFINE VARIABLE oMessages AS JsonArray NO-UNDO. oRequest = NEW JsonAPIRequestParser(oJsonInput). oBody = oRequest:getPayload(). // obtem o nome da propriedade que ocorreu o LEAVE para validacao cProp = oBody:getCharacter("property") NO-ERROR. oValue = oBody:getJsonObject("value") NO-ERROR. cValue = STRING(oValue:GetCharacter(cProp)) NO-ERROR. cId = oValue:getCharacter("id") NO-ERROR. /* Recebemos do HTML o JSON abaixo { "property": "codAcoes", "value": { "codIdiomPadr": "01 Português", "codIdioma": "12345678", "desIdioma": "12345678901234567890", "id": 6, } } */ // Novas Acoes sobre os campos da tela // oNewValue guarda os valores a serem especificados para os campos ASSIGN oNewValue = NEW JsonObject(). // oNewFields guarda a lista de campos que serao alterados/modificados ASSIGN oNewFields = NEW JsonArray(). // cFocus especifica em qual campo sera feito o focus ASSIGN cFocus = cProp. // oMessages guarda as mensagens de retorno formato // { code: '00', message: 'texto', detailedMessage: 'detalhes da mensagem' } ASSIGN oMessages = NEW JsonArray(). // // adicinar a logica de validacoes dos campos aqui // ASSIGN oRet = NEW JsonObject(). // value -> contem todos os valores dos campos de tela oRet:add('value', oNewValue). // fields -> contem a lista de campos com suas novas propriedades oRet:add('fields', oNewFields). // focus -> especifica em qual campo o cursor vai ficar posicionado oRet:add('focus', cFocus). // _messages -> contem uma lista de mensagens que vao aparecer como notificacoes oRet:add('_messages', oMessages). oResponse = NEW JsonAPIResponse(oRet). oJsonOutput = oResponse:createJsonResponse(). END PROCEDURE. PROCEDURE piSetPersonalizationData: DEFINE INPUT PARAMETER hTab AS HANDLE NO-UNDO. DEFINE INPUT PARAMETER oBody AS JsonObject NO-UNDO. FOR EACH ttPersonalization: ASSIGN cFld = ttPersonalization.codField cType = ttPersonalization.codType. CASE cType: WHEN "string" THEN hTab:BUFFER-FIELD(cFld):buffer-value() = oBody:getCharacter(cFld) NO-ERROR. WHEN "number" THEN hTab:BUFFER-FIELD(cFld):buffer-value() = oBody:getInteger(cFld) NO-ERROR. WHEN "currency" THEN hTab:BUFFER-FIELD(cFld):buffer-value() = oBody:getDecimal(cFld) NO-ERROR. WHEN "boolean" THEN hTab:BUFFER-FIELD(cFld):buffer-value() = oBody:getLogical(cFld) NO-ERROR. WHEN "datetime" THEN hTab:BUFFER-FIELD(cFld):buffer-value() = oBody:getDatetime(cFld) NO-ERROR. WHEN "date" THEN hTab:BUFFER-FIELD(cFld):buffer-value() = oBody:getDate(cFld) NO-ERROR. END CASE. END. END PROCEDURE. /* fim */ |
Caso seja enviado valores da área de negócio que não estejam cadastrados como campos personalizados, o PO-UI, o padrão adicionará essa informação extra na tela, onde será apresentado em formato String sem uma label válida. |
Os fontes de exemplo para a personalização também estão disponíveis em nosso GIT (fwk-totvs-jille) em "LINKS ÚTEIS". |
fwk-tools-jille/DATASUL/personalization-poui/ ( https://github.com/totvs/fwk-tools-jille )
A ideia era apresentar uma técnica de construção para a personalização de um programa TOTVS da Liinha Datasul, de forma segura e simples.
Esta documentação trata-se de um MVP, que está sendo continuamente evoluída em nossas Sprints (SQUAD TOOLS).
<!-- esconder o menu --> <style> div.theme-default .ia-splitter #main { margin-left: 0px; } .ia-fixed-sidebar, .ia-splitter-left { display: none; } #main { padding-left: 10px; padding-right: 10px; overflow-x: hidden; } .aui-header-primary .aui-nav, .aui-page-panel { margin-left: 0px !important; } .aui-header-primary .aui-nav { margin-left: 0px !important; } </style> |