` Logging data changes on field by field basis (Roberto Artigas Jr) - Icetips Article
Icetips - Templates, Tools & Utilities for Clarion Developers

Templates, Tools and Utilities
for Clarion Developers

Icetips Article

Back to article list   Search Articles     Add Comment     Printer friendly     Direct link  

Templates: Logging data changes on field by field basis
2000-12-31 -- Roberto Artigas Jr
 
Newsgroups: TopSpeed.Topic.Templates Heavenes - There have been several questions in the Newsgroups on how to do field by field file maintenance logging. So I thought I would post the solution I arrived at. The technique reguires some understanding of the underlying pieces that are used. Some of the pieces can not be posted. However, I will explain the pieces that are not shown and I have included a template that is used to perform Field by Field Validation and tie all the pieces together. Template writters will no doubt be able to improve the techniques I used and provide better solutions. All I ask is that you post to the newsgroups improvements and/or your solutions. I really would like some constructive feedback/criticism since this involved quite a bit of work. I am sure it can be done better and made more automatic, and if you can do this, please post the enhanced solution. The process is as follows. You go into the browse select a record and make your changes for as many records as you want. The changes are logged to a file. You have to define your file. Basically it contains keys to identify the id, file, field, user, field before, field after, and a flag to say whether it has been printed already or not. Once your are done and exit the Browse, the exit procedure checks a control record to see if a report is to be printed (P), if you keep the changes on disk (D), or do both (B). At that point you get a report preview that shows you your changes which you can print or ignore. For all this to work the deletes have to be selected as to happen automatic. The log will reflect all the ADD's, CHANGE's and DELETE's. The solution involves a template for the browse and a template for the form. They are fully commented so you folks can understand what is going on. You will NOT be able to use the templates as they are. You will HAVE to do some work. You will BE able to see how to put it all together. Things that are NOT shown: ------------------------- 1) The actual LOG file structure. 2) The control record that determines the action (D,P,B) for this program. It is just a big array with equates for each program that you want to have an option to log. 3) 'PrintAuditLogIndividual' - The process that prints the audit report. 4) 'UpdateAuditLogIndividual' - The process that updates the print flag. 5) 'DeleteAuditLogIndividual' - The process that deletes the records only to be printed. 6) 'fnF00R06Get' - The routine that gets the control records. 7) 'fnLog_Audit' - The routine that actually does the log of the records. Now here is the templates: #!==================================================================== #! File: AppMaint.Tpl #! Purpose: Templates To Support Maintenance Logging #! Author: 1998.12.21 - Roberto Artigas Jr #!==================================================================== #TEMPLATE(zAppMaint, 'Maintenance Templates'),FAMILY('CW20','ABC') #!==================================================================== #!==================================================================== #!==================================================================== #!==================================================================== #!==================================================================== #!==================================================================== #EXTENSION(Maint00, 'Maint00: Helper Template'),PROCEDURE #!==================================================================== #! This is used in the BROWSE portion of the BROWSE/FORM maintenance. #!==================================================================== #LOCALDATA #ENDLOCALDATA #!==================================================================== #RESTRICT,WHERE(%Window) #ENDRESTRICT #SHEET,HSCROLL #TAB('&General') #BOXED('Purpose') #DISPLAY('This template adds the HOOKS into the maintenance') #DISPLAY('routines and creates code needed.') #DISPLAY('It is a helper routine for the simple cases.') #ENDBOXED #ENDTAB #TAB('&Parameters') #BOXED('Audit') #PROMPT('Maintenance File:',FROM(%File)),%rptFile,REQ,DEFAULT('') #PROMPT('Maintenance Menu:',@S6),%rptMenu,REQ,DEFAULT('#06.??') #PROMPT('Maintenance Title:',@S40),%rptTitle,REQ,DEFAULT('??? Maintenance Log Report') #ENDBOXED #BOXED('Browse') #PROMPT('Browse Name:',@S20),%rptBRW1,REQ,DEFAULT('BRW1') #ENDBOXED #ENDTAB #ENDSHEET #!==================================================================== #AT(%DataSection) !BOE:DataSection ! Control File Record 06 is the record that contains the ! logging options for all programs. The options are: ! D=Disk Only, P=Print Only, B=Both. The 'What' variable ! has the option for this program. LOC:What STRING('B') LOC:Rec00R06 GROUP(F000_R06) END LOC:Mem00R06 GROUP(M000_R06) END !EOE:DataSection #ENDAT #!==================================================================== #AT (%ProcedureInitialize) !BOE:ProcedureInitialize ! This will read Control File Record 06 into memory so it ! can set the logging option for this program. ! If no record exists then the program will default to both. ! If the record exits the programmer is responsible for ! filling the embed with the correct array value that will ! set the option for this particular program. IF ~fnF00R06Get(LOC:Rec00R06, LOC:Mem00R06) THEN LOC:What = 'B' ELSE #EMBED(%SetWhatOption,'LOC:What = LOC:Mem00R06.MtLog[MT06:F???_???]') END !EOE:ProcedureInitialize #ENDAT #!==================================================================== #!AT(%AfterWindowOpening) #AT(%WindowManagerMethodCodeSection,'Init','(),BYTE'),PRIORITY(8800) !BOE:AfterWindowOpening ! Check the security level for this user. If it is less than ! entry then hide the insert and delete. Then change the ! Change to Inquiry. The Form will get modified also by the ! corresponing template to this one. ! We also set the browse control to NO buttons. This disables ! the pop-up (right click) menu controls. IF (GLO:SecLevel < '2') THEN %rptBRW1.InsertControl = 0 %rptBRW1.DeleteControl = 0 HIDE(?Insert:3) HIDE(?Delete:3) ?Change:3{PROP:Text} = 'In&quiry' END !EOE:AfterWindowOpening #ENDAT #!==================================================================== #AT(%BeforeWindowClosing) !BOE:BeforeWindowClosing ! When we come back from the form we check the audit option. ! Based on the option we perform the routines bellow. These ! routines are located in a library at a lower level. ! The routine names describe their function. CASE (UPPER(CLIP(LOC:What))) OF 'B' PrintAuditLogIndividual('%rptFile', GLO:User_Id, '%rptMenu', '%rptTitle') UpdateAuditLogIndividual(LOC:What, '%rptFile', GLO:User_Id) OF 'D' OF 'P' PrintAuditLogIndividual('%rptFile', GLO:User_Id, '%rptMenu', '%rptTitle') DeleteAuditLogIndividual(LOC:What, '%rptFile', GLO:User_Id) END !EOE:BeforeWindowClosing #ENDAT #!==================================================================== #AT (%EndOfProcedure) #ENDAT #!==================================================================== #!==================================================================== #!==================================================================== #EXTENSION(Update00, 'Update00: Helper Template'),PROCEDURE #!==================================================================== #! This is used in the FORM portion of the BROWSE/FORM maintenance. #!==================================================================== #PREPARE #DECLARE(%FieldTypes),UNIQUE #ADD(%FieldTypes,'A2') #ADD(%FieldTypes,'A7') #ADD(%FieldTypes,'D4') #ADD(%FieldTypes,'MY') #ADD(%FieldTypes,'N6') #ADD(%FieldTypes,'P2') #ADD(%FieldTypes,'P3') #ADD(%FieldTypes,'R6') #ADD(%FieldTypes,'S') #ADD(%FieldTypes,'T2') #ADD(%FieldTypes,'T3') #ADD(%FieldTypes,'U4') #ENDPREPARE #!==================================================================== #LOCALDATA #ENDLOCALDATA #!==================================================================== #RESTRICT,WHERE(%Window) #ENDRESTRICT #SHEET,HSCROLL #TAB('&General') #BOXED('Purpose') #DISPLAY('This template adds the HOOKS into the maintenance') #DISPLAY('routines and creates code needed.') #DISPLAY('It is a helper routine for the simple cases.') #ENDBOXED #ENDTAB #TAB('&File') #BOXED('') #PROMPT('File Name:',FROM(%File)),%logFile,REQ,DEFAULT('') #PROMPT('File Prefix:',@S4),%logPrefix,REQ,DEFAULT('') #DISPLAY('Note: File Name And File Prefix MUST Correspond.') #ENDBOXED #ENDTAB #TAB('Fi&elds') #BOXED('') #BUTTON('Field and Field Type Pairs'),AT(,,173,),MULTI(%FTPair,%AField & ' ' & %AType) #PROMPT('Field Name:',@S20),%AField,REQ,UNIQUE,DEFAULT('') #PROMPT('Field Type:',FROM(%FieldTypes)),%AType,REQ,DEFAULT('') #DISPLAY #DISPLAY('A2 = @n-18.2 [Amounts]') #DISPLAY('A7 = @n-18.7 [Dividend/Earning]') #DISPLAY('D4 = Date [STRING 4 Bytes]') #DISPLAY('MY = @p##/##p [Month/Year]') #DISPLAY('N6 = @n06 [BYTE,SHORT]') #DISPLAY('P2 = @n-7.2 [Percentage]') #DISPLAY('P3 = @n-10.3 [Percentage]') #DISPLAY('R6 = @n-16.6 [Rates]') #DISPLAY('S = Strings') #DISPLAY('T2 = Time [SHORT]') #DISPLAY('T3 = Time [STRING 3 Bytes]') #DISPLAY('U4 = @n-16.4 [Units]') #DISPLAY #ENDBUTTON #DISPLAY('You have to tell me exactly what fields and their types') #DISPLAY('that you want to audit with this program.') #DISPLAY('') #PROMPT('Key Name:',@S20),%KField,REQ,DEFAULT('') #DISPLAY('You have to tell me what key is the key field for this') #DISPLAY('File. If it is a compound key, you have to tell me the') #DISPLAY('name of the local variable. Enter this exactly as you see') #DISPLAY('the field name or the local variable.') #ENDBOXED #ENDTAB #TAB('&Read Only') #BOXED('') #BUTTON('Read-Only Objects On Screen'),AT(,,173,),MULTI(%ScrField,%SField) #PROMPT('Screen Object Name:',@S20),%SField,REQ,UNIQUE,DEFAULT('') #ENDBUTTON #DISPLAY('') #DISPLAY('These are additional fields, controls, etc, that need to') #DISPLAY('read-only when user DOES NOT have enough security.') #DISPLAY('') #DISPLAY('The NORMAL file fields are automatically selected and') #DISPLAY('read-only when necessary. All the fancy user aids, like') #DISPLAY('calendar buttons, radio buttons, etc, you need to tell') #DISPLAY('me about since they are not known to me.') #ENDBOXED #ENDTAB #TAB('&Disable') #BOXED('') #BUTTON('Disable Objects On Screen'),AT(,,173,),MULTI(%DisField,%DField) #PROMPT('Screen Object Name:',@S20),%DField,REQ,UNIQUE,DEFAULT('') #ENDBUTTON #DISPLAY('') #DISPLAY('These are additional fields, controls, etc, that need to') #DISPLAY('disabled when user DOES NOT have enough security.') #DISPLAY('') #DISPLAY('The NORMAL file fields are automatically selected and') #DISPLAY('disabled when necessary. All the fancy user aids, like') #DISPLAY('calendar buttons, radio buttons, etc, you need to tell') #DISPLAY('me about since they are not known to me.') #ENDBOXED #ENDTAB #ENDSHEET #!==================================================================== #AT(%DataSection) !BOE:DataSection ! Control File Record 06 is the record that contains the ! logging options for all programs. The options are: ! D=Disk Only, P=Print Only, B=Both. The 'What' variable ! contains the option for this program. The 'Action' area ! is 'A'=Add, 'C'=Change, and 'D'=Delete. The 'DoneIt' is ! set to 'Y' if we pressed Ok. The BEG and END are the ! definitions for the record before and after changes. ! This way we can compare fields for logging. LOC:Rec00R06 GROUP(F000_R06) END LOC:Mem00R06 GROUP(M000_R06) END LOC:What STRING('B') LOC:Action STRING('C') LOC:DoneIt STRING('N') LOC:BEG:%logPrefix:Record LIKE(%logPrefix:Record) LOC:END:%logPrefix:Record LIKE(%logPrefix:Record) !EOE:DataSection #ENDAT #!==================================================================== #AT (%ProcedureInitialize) !BOE:ProcedureInitialize ! This checks what the user is doing and initializes the ! necessary areas. Please note that dates in our case ! require additional initialization to binary zeros since ! they are defined in the file as strings. ! The embeds after the clear dates are there for any ! additional initialization of date work areas. CASE(SELF.Request) OF InsertRecord LOC:Action = 'A' LOC:DoneIt = 'N' CLEAR(LOC:BEG:%logPrefix:Record) #FOR(%FTPair) #IF(%AType='D4') LOC:BEG:%logPrefix:Record.%logPrefix:%AField = '<0,0,0,0>' #ENDIF #ENDFOR #EMBED(%InsertClearDateAreas,'Insert Clear Date Areas') OF ChangeRecord LOC:Action = 'C' LOC:DoneIt = 'N' LOC:BEG:%logPrefix:Record :=: %logPrefix:Record OF DeleteRecord LOC:Action = 'D' LOC:DoneIt = 'Y' LOC:BEG:%logPrefix:Record :=: %logPrefix:Record CLEAR(LOC:END:%logPrefix:Record) #FOR(%FTPair) #IF(%AType='D4') LOC:END:%logPrefix:Record.%logPrefix:%AField = '<0,0,0,0>' #ENDIF #ENDFOR #EMBED(%DeleteClearDateAreas,'Delete Clear Date Areas') END !EOE:ProcedureInitialize #ENDAT #!==================================================================== #AT (%WindowManagerMethodCodeSection,'Run','(),BYTE'),PRIORITY(4000) !BOE:WindowManagerMethodCodeSection ! On the form you have to set to 'Automatic Delete'. ! Once you do that this will check if you truly want to ! delete the record. You do not it will exit. ! The additional embeds are there for any additional checking ! you want to do after the Yes/No response. IF (GlobalRequest = DeleteRecord) THEN CASE MESSAGE('Are You Sure You Want To Delete This Record?', | 'Delete Record', ICON:Question, | BUTTON:Yes+BUTTON:No, BUTTON:No, 1) OF BUTTON:No #EMBED(%DeleteNo,'Delete No') RETURN(0) OF BUTTON:Yes #EMBED(%DeleteYes,'Delete Yes') END END #!EMBED(%MoreDeleteChecks,'More Delete Checks') ! In the embed below you can check for any additional ! conditions before you delete this record. Is it used? ! What other records is it related to? Etc. !EOE:WindowManagerMethodCodeSection #ENDAT #!==================================================================== #AT(%AfterWindowOpening) #DECLARE(%theField) #DECLARE(%AllFiles),UNIQUE,MULTI #FOR(%File) #ADD(%AllFiles,%File) #ENDFOR !BOE:AfterWindowOpening ! If we are doing a change or delete we make sure the keys ! can not be changed by changing the field properties on ! the screen. The additional embeds are for fields on the ! screen that might be work areas that you want to protect. CASE(SELF.Request) OF InsertRecord OF ChangeRecord #FOR(%AllFiles) #FIX(%File,%AllFiles) #IF(%File=%logFile) #FOR(%Key) #FOR(%KeyField) #IF(INSTRING('NOPOPULATE',%FieldQuickOptions,1,1)=0) ?%[19]KeyField {PROP:ReadOnly} = TRUE ?%[19]KeyField {PROP:Skip} = TRUE #ENDIF #ENDFOR #ENDFOR #ENDIF #ENDFOR #EMBED(%ChangeKeyReadOnly,'Change Key Read Only') OF DeleteRecord #FOR(%AllFiles) #FIX(%File,%AllFiles) #IF(%File=%logFile) #FOR(%Key) #FOR(%KeyField) #IF(INSTRING('NOPOPULATE',%FieldQuickOptions,1,1)=0) ?%[19]KeyField {PROP:ReadOnly} = TRUE ?%[19]KeyField {PROP:Skip} = TRUE #ENDIF #ENDFOR #ENDFOR #ENDIF #ENDFOR #EMBED(%DeleteKeyReadOnly,'Delete Key Read Only') END ! You better had not changed the tag of the OK button. ! If the security level is too low we hide the OK button. ! Even if everything is protected in the screen and you ! press ok it trigers the audit. We don't want to do that. ! Three template areas are used in here. The audit field ! list gets set up, the screen field list gets set up, ! and the disable list gets set up. The screen field list ! is used for entry fields on the screen that are not on ! the record. The disable field list gets used for lookup ! buttons and radio buttons. And the audit field list is ! used for the fields on the record you want to audit. ! BOTTOM LINE: Your security is too low, you can look at ! the data and press Close/Cancel only. Them's the breaks. IF (GLO:SecLevel < '2') THEN HIDE(?OK) #! #FOR(%AllFiles) #! #FIX(%File,%AllFiles) #! #IF(%File=%logFile) #! #FOR(%Field) #! #IF(INSTRING('NOPOPULATE',%FieldQuickOptions,1,1)=0) #! ?%[19]Field {PROP:ReadOnly} = TRUE #! ?%[19]Field {PROP:Skip} = TRUE #! #ENDIF #! #ENDFOR #! #ENDIF #! #ENDFOR #FOR(%FTPair) #SET(%theField,%logPrefix & ':' & %AField) ?%[19]theField {PROP:ReadOnly} = TRUE ?%[19]theField {PROP:Skip} = TRUE #ENDFOR #FOR(%ScrField) ?%[19]SField {PROP:ReadOnly} = TRUE ?%[19]SField {PROP:Skip} = TRUE #ENDFOR #FOR(%DisField) ?%[19]DField {PROP:Disable} = TRUE #ENDFOR #EMBED(%SecurityReadOnly,'Security Read Only') END !EOE:AfterWindowOpening #ENDAT #!==================================================================== #AT(%ControlEventHandling,'?OK','Accepted') !BOE:ControlEventHandling ! You better had not changed the tag of the OK button. ! When you press OK, 'DoneIt' gets set to 'Y'. Then depending ! on the action, the after record gets set up. On exit we ! will compare the BEG and END records field by field and ! logging the ones that are different. LOC:DoneIt = 'Y' CASE (LOC:Action) OF 'A' LOC:END:%logPrefix:Record :=: %logPrefix:Record OF 'C' LOC:END:%logPrefix:Record :=: %logPrefix:Record OF 'D' END !EOE:ControlEventHandling #ENDAT #!==================================================================== #AT(%BeforeWindowClosing) !BOE:BeforeWindowClosing ! Check if we have pressed OK. If we have, then log all fields ! that are defined. If we ESCaped nothing is done. IF (LOC:DoneIt = 'Y') THEN ! This will read Control File Record 06 into memory so it ! can set the logging option for this program. ! If no record exists then the program will default to both. ! If the record exits the programmer is responsible for ! filling the embed with the correct array value that will ! set the option for this particular program. IF ~fnF00R06Get(LOC:Rec00R06, LOC:Mem00R06) THEN LOC:What = 'B' ELSE #EMBED(%SetWhatOption,'LOC:What = LOC:Mem00R06.MtLog[MT06:F???_???]') END ! The embed for compound keys allow you to append together ! multiple keys into one area that will be added to the file. ! Please note that the key area defined in the template is ! a field on the file or a string variable already defined. #EMBED(%SetUpCompoundKeys,'Set Up Compound Keys') ! Pump out all the fields that have been selected for audit. ! This uses the Audit Field List for this purpose passing it ! to the 'fnLog_Audit' routine. This routine is defined in ! a library at a lower level. #FOR(%FTPair) fnLog_Audit(LOC:What, '%AType', '%logFile', GLO:User_Id, LOC:Action, %KField, '%AField', | LOC:BEG:%logPrefix:Record.%logPrefix:%AField, LOC:END:%logPrefix:Record.%logPrefix:%AField) #ENDFOR ! This embed is for doing additional field maintenance ! that does not fit the normal flow of the template. #EMBED(%DoFieldMaintenanceLogs,'Do Field Maintenance Logs') END !EOE:BeforeWindowClosing #ENDAT #!==================================================================== #AT (%EndOfProcedure) #ENDAT #!==================================================================== #!==================================================================== #!==================================================================== #!==================================================================== #!==================================================================== #!====================================================================


Today is April 25, 2024, 9:48 pm
This article has been viewed 35110 times.
Google search has resulted in 36 hits on this article since January 25, 2004.



Back to article list   Search Articles   Add Comment   Printer friendly

Login

User Name:

Password: