Convertendo SmartObjects para DBO 2.0
Este capítulo é destinado ao desenvolvedor que utiliza SmartObjects e deseja convertê-los para DBOs 2.0.
Para converter um SmartObject devem ser seguidos os seguintes passos: Criar um novo DBO 2.0;
Transferir lógicas dos métodos dos SmartObjects para o DBO 2.0. A seguir são detalhados os passos:
Criar o novo DBO
Este passo está detalhado no item 'Construindo o DBO'.
Transferir lógicas dos métodos dos SmartObjects para o DBO
2.0
Quando se trabalha com SmartObjects, a lógica de navegação, leitura, consulta, alteração, etc. está em diferentes programas. Por exemplo: a lógica de navegação está na SmartQuery, a lógica de gravação está em várias SmartViewers, etc.
Mas ao utilizar DBOs toda a lógica de tratamento de registro encontra-se no DBO. Através disto, basta o desenvolvedor transferir as lógicas dos
SmartObjects para os DBOs, conforme descrito a seguir: SmartQuery
Toda a lógica de abertura de query deve ser transferida para os métodos openQuery<Description>. Além disso, caso a SmartQuery faça uso de mais de
uma tabela, isto deve ser substituído pelo uso do método setConstraint<Description> e linkTo<Description>.
E, ainda, todas as restrições impostas à abertura da query, também devem ser transferidas para seus respectivos métodos setConstraint<Description>. A seguir, um exemplo, no qual a SmartQuery possuía a tabela Customer e a tabela SalesRep, como tabela estrangeira.
DEFINE VARIABLE cSales-Rep LIKE Customer.Sales-Rep NO-UNDO. PROCEDURE openQueryMain:
IF cSales-Rep = "" THEN OPEN QUERY {&QueryName}
FOR EACH Customer NO-LOCK. ELSE
OPEN QUERY {&QueryName} FOR EACH Customer
WHERE Customer.Sales-Rep = cSales-Rep NO-LOCK.
END PROCEDURE.
PROCEDURE openQueryByName: IF cSales-Rep = "" THEN OPEN QUERY {&QueryName}
FOR EACH Customer NO-LOCK BY Customer.Name. ELSE
OPEN QUERY {&QueryName} FOR EACH Customer
WHERE Customer.Sales-Rep = cSales-Rep NO-LOCK BY Customer.Name.
END PROCEDURE.
PROCEDURE openQuerySalesRep: OPEN QUERY {&QueryName} FOR EACH Customer
WHERE Customer.Sales-Rep = cSales-Rep NO-LOCK.
END PROCEDURE.
PROCEDURE setConstraintSalesRep:
DEFINE INPUT PARAMETER pSales-Rep LIKE Customer.Sales-Rep NO- UNDO.
ASSIGN cSales-Rep = pSales-Rep.
RETURN "OK":U. END PROCEDURE.
PROCEDURE linkToSalesRep:
DEFINE INPUT PARAMETER pHandle AS HANDLE NO-UNDO.
CAPÍTULO 5 Convertendo SmartObjects para DBO 2.0 59
RUN getKey IN pHandle (OUTPUT cSalesAux).
RUN setConstraintSalesRep IN THIS-PROCEDURE (INPUT cSalesAux). END PROCEDURE.
Neste exemplo, foram definidos 3 (três) métodos de abertura de query, pois o DBO normalmente é único por tabela. Sendo assim, criou-se os métodos setConstraintSalesRep e linkToSalesRep.
Estes métodos serão utilizados para fazer a comunicação entre um DBO Pai (SalesRep) e um DBO Filho (Customer), porém o DBO Filho também terá opções de aberturas independente do DBO Pai.
SmartViewer
Toda a lógica de gravação da viewer deve ser transferida para os métodos before/afterCreateRecord ou before/afterUpdateRecord.
A seguir, um exemplo, no qual a SmartViewer fazia a gravação automática do campo cust-num no momento de criação do registro.
Método Antigo:
DEFINE BUFFER bfCustomer FOR Customer. PROCEDURE local-create-record:
DEFINE VARIABLE iCust-Num AS INTEGER NO-UNDO.
FIND LAST bfCustomer NO-LOCK NO-ERROR. IF AVAILABLE bfCustomer THEN
ASSIGN iCust-Num = bfCustomer.Cust-Num + 1. ELSE
ASSIGN iCust-Num = 1.
RUN dispatch IN THIS-PROCEDURE ("create-record":U). IF RETURN-VALUE = "ADM-ERROR":U THEN
RETURN "ADM-ERROR":U.
ASSIGN Customer.Cust-Num = iCust-Num. END PROCEDURE.
Método Novo:
DEFINE BUFFER bfCustomer FOR Customer. PROCEDURE beforeCreateRecord:
DEFINE VARIABLE iCust-Num AS INTEGER NO-UNDO.
FIND LAST bfCustomer NO-LOCK NO-ERROR. IF AVAILABLE bfCustomer THEN
ASSIGN iCust-Num = bfCustomer.Cust-Num + 1. ELSE
ASSIGN RowObject.Cust-Num = iCust-Num. RETURN "OK":U.
END PROCEDURE.
Neste exemplo, foi definido o método beforeCreateRecord para realizar a atualização do campo cust-num. Vale salientar que somente será atualizado este campo se for feita uma criação.
A seguir, um exemplo, no qual são feitas atualizações comuns a criação e alteração de registro da tabela Customer:
Método Antigo:
PROCEDURE local-assign-record:
RUN dispatch IN THIS-PROCEDURE ("assign-record":U). IF RETURN-VALUE = "ADM-ERROR":U THEN
RETURN "ADM-ERROR":U.
ASSIGN Customer.Credit-Limit = Customer.Credit-Limit * 1.10. END PROCEDURE.
Método Novo:
PROCEDURE before_copyTT2Buffer:
ASSIGN RowObject.Credit-Limit = RowObject.Credit-Limit * 1.10.
RETURN "OK":U. END PROCEDURE.
Neste exemplo, criou-se o método before_copyTT2Buffer. O qual é executado antes da transferência dos dados da temp-table RowObject para a tabela principal do DBO.
A seguir, um exemplo, no qual são feitas validações de criação de registro: Método Antigo:
PROCEDURE local-assign-record:
DEFINE VARIABLE iCust-Num AS INTEGER NO-UNDO.
ASSIGN iCust-Num = INTEGER(Customer.Cust-Num:SCREEN-VALUE IN FRAME {&FRAME-NAME}).
IF adm-new-record THEN
IF CAN-FIND(Customer WHERE Customer.Cust-Num = iCustNum) THEN DO:
RUN utp/ut-msgs.p (INPUT "SHOW":U, INPUT <ErrorNumber> INPUT <ErrorParameters>). RETURN "ADM-ERROR":U.
CAPÍTULO 5 Convertendo SmartObjects para DBO 2.0 61
RUN dispatch IN THIS-PROCEDURE ("assign-record":U). IF RETURN-VALUE = "ADM-ERROR":U THEN
RETURN "ADM-ERROR":U. END PROCEDURE.
OBSERVAÇÃO: O utilitário UTP/UT-MSGS.P era utilizado em SmartObjects, porém, não deverá ser utilizado em DBOs, conforme conceito da tecnologia de DBOs.
Método Novo:
PROCEDURE beforeCreateRecord:
IF CAN-FIND(Customer WHERE Customer.Cust-Num =
RowObject.Cust-Num) THEN DO: {method/svc/errors/inserr.i &ErrorNumber="9999" &ErrorType="EMS" &ErroSubType="ERRO" &ErrorParameters="''"} RETURN "NOK":U. END. RETURN "OK":U. END PROCEDURE.
Neste exemplo, foi definido o método beforeCreateRecord para realizar a validação de registro duplicado. Vale salientar que somente será executada esta validação quando for feita a criação de registros.
A seguir, um exemplo, no qual são feitas validações comuns à criação e alteração de registro:
Método Antigo:
PROCEDURE pi-validate: {include/i-vldfrm.i}
IF Customer.Name = "":U THEN DO: {include/i-vldprg.i}
RUN utp/ut-msgs.p (INPUT "SHOW":U, INPUT <ErrorNumber>, INPUT <ErrorParameters>).
APPLY "ENTRY":U TO Customer.Name IN FRAME {&FRAME-NAME}.
RETURN "ADM-ERROR":U. END.
OBSERVAÇÃO: O utilitário UTP/UT-MSGS.P era utilizado em SmartObjects, porém, não deverá ser utilizado em DBOs, conforme conceito da tecnologia de DBOs.
Método Novo:
PROCEDURE validateRecord:
IF RowObject.Name = "":U THEN {method/svc/errors/inserr.i &ErrorNumber="9999" &ErrorType="EMS" &ErroSubType="ERROR" &ErrorParameters="''"}
IF CAN-FIND(FIRST RowErrors) THEN RETURN "NOK":U.
RETURN "OK":U. END PROCEDURE.
Neste exemplo, foi definido o método validateRecord para realizar a validação sobre o campo name. Vale salientar que este método será executado tanto na criação como na alteração de registros.
A seguir, um exemplo, no qual são feitas validações de eliminação de registro: Método Antigo:
PROCEDURE local-delete-record:
IF CAN-FIND(FIRST Order OF Customer) THEN DO: RUN utp/ut-msgs.p (INPUT "SHOW":U, INPUT <ErrorNumber>, INPUT <ErrorParameters>). RETURN "ADM-ERROR":U. END.
RUN dispatch IN THIS-PROCEDURE ("delete-record":U). END PROCEDURE.
OBSERVAÇÃO: O utilitário UTP/UT-MSGS.P era utilizado em SmartObjects, porém, não deverá ser utilizado em DBOs, conforme conceito da tecnologia de DBOs.
Método Novo:
PROCEDURE beforeDeleteRecord:
IF CAN-FIND(FIRST Order OF RowObject) THEN {method/svc/errors/inserr.i
CAPÍTULO 5 Convertendo SmartObjects para DBO 2.0 63
&ErrorType="EMS" &ErroSubType="ERRO" &ErrorParameters="''"}
IF CAN-FIND(FIRST RowErrors) THEN RETURN "NOK":U.
RETURN "OK":U. END PROCEDURE.
Neste exemplo, foi definido o método beforeDeleteRecord para realizar a validação a fim de verificar se o registro pode ser eliminado.
Triggers de Dicionário
Quando utilizada uma trigger de create, toda a lógica deve ser transferida para o método beforeCreateRecord.
A seguir, um exemplo, no qual são setados valores automaticamente para a tabela Customer:
Método Antigo:
TRIGGER PROCEDURE FOR create OF customer.
ASSIGN Customer.Cust-Num = NEXT-VALUE(next-cust-num). Método Novo:
DEFINE BUFFER bfCustomer FOR Customer. PROCEDURE beforeCreateRecord:
FIND LAST bfCustomer NO-LOCK NO-ERROR. IF AVAILABLE bfCustomer THEN
ASSIGN RowObject.Cust-Num = bfCustomer.Cust-Num + 1. ELSE
ASSIGN RowObject.Cust-Num = 1.
RETURN "OK":U. END PROCEDURE.
Neste exemplo, foi criado o método beforeCreateRecord para realizar a gravação automática do campo cust-num, no momento de criação do registro. Quando utilizada uma trigger de delete, toda a lógica de validação deve ser transferida para o método beforeDeleteRecord e a lógica de eliminação em cascata deve ser transferida para o método afterDeleteRecord.
A seguir, um exemplo, no qual é feita a eliminação em cascata de registros: Método Antigo:
TRIGGER PROCEDURE FOR delete OF customer. FOR EACH Order OF Customer:
DELETE Order. END.
Método Novo:
PROCEDURE afterDeleteRecord: FOR EACH Order OF RowObject: DELETE Order.
END.
RETURN "OK":U. END PROCEDURE.
Neste exemplo, foi criado o método afterDeleteRecord para realizar a eliminação em cascata da tabela Order.
Quando utilizado uma trigger de write, toda a lógica de validação deve ser transferida para o método validateRecord (quando estas forem comuns à criação e alteração) ou beforeUpdateRecord (quando estas forem pertinentes à alteração) .
A lógica de atualização deve ser transferida para o método
before_copyTT2Buffer (quando estas forem comuns à criação e alteração) ou para o método beforeUpdateRecord (quando estas foram pertinentes à alteração) ou para o método afterUpdateRecord (quando estas foram atualizações em cascata).
A seguir, um exemplo, no qual são feitas validações comuns à criação e alteração; atualizações automáticas de campos da tabela customer; atualizações em cascata de outras tabelas:
Método Antigo:
TRIGGER PROCEDURE FOR write OF customer OLD BUFFER oldcustomer. IF Customer.Name = "":U THEN DO:
RUN utp/ut-msgs.p (INPUT "SHOW":U, INPUT <ErrorNumber>, INPUT <ErrorParameters>). RETURN ERROR.
END.
IF Customer.Address2 = "":U THEN
ASSIGN Customer.Address2 = Customer.Address.
IF Customer.Cust-Num <> oldCustomer.Cust-Num THEN FOR EACH Order OF oldCustomer:
ASSIGN Order.Cust-Num = Customer.Cust-Num. END.
CAPÍTULO 5 Convertendo SmartObjects para DBO 2.0 65
OBSERVAÇÃO: O utilitário UTP/UT-MSGS.P era utilizado em SmartObjects, porém, não deverá ser utilizado em DBOs, conforme conceito da tecnologia de DBOs.
Método Novo:
PROCEDURE validateRecord:
IF RowObject.Name = "":U THEN {method/svc/errors/inserr.i &ErrorNumber="9999" &ErrorType="EMS" &ErroSubType="ERRO" &ErrorParameters="''"}
IF CAN-FIND(FIRST RowErrors) THEN RETURN "NOK":U.
RETURN "OK":U. END PROCEDURE.
PROCEDURE before_copyTT2Buffer: IF RowObject.Address2 = "":U THEN
ASSIGN RowObject.Address2 = RowObject.Adress.
RETURN "OK":U. END PROCEDURE.
PROCEDURE beforeUpdateRecord:
IF RowObject.Cust-Num <> Customer.Cust-Num THEN FOR EACH Order OF Customer:
ASSIGN Order.Cust-Num = RowObject.Cust-Num. END.
END PROCEDURE.
Neste exemplo, foi criado o método validateRecord para realizar validações comuns à criação e alteração de registro. E ainda, o método
before_copyTT2Buffer para realizar atualizações comuns à criação e alteração. E, além disso, o método beforeUpdateRecord para realizar atualizações em cascata pertinentes à alteração.
Observação: Ao retirar as lógicas de negócio das triggers de dicionário, deve- se manter as chamadas de UPCs nas triggers.
SmartBrowse
Os SmartBrowses devem ser analisados conforme sua funcionalidade.
Caso o SmartBrowse seja utilizado para navegação, deve-se seguir as mesmas regras sugeridas para SmartQuery. Mas, caso o SmartBrowse seja utilizado
para update (inclusão/alteração/exclusão) deve-se seguir as mesmas regras sugeridas para SmartViewer.
67