I have on a project a Journal table where i write the modifications Just send the journal to another system and recover it to replicate all the changes made on the first computer.
I have on every table to sync a field named SYNCTAG C(12) with the DefaultValue versynccode("TABLENAME") The default value is SYS(2015) The table, on the description field has the following text: TITLE=TABLENAME SHARED=YES/NO KEYFIELD=KEYFIELDS And on the triggers field: Insert trigger: jrn("tablename",1) Update trigger: jrn("tablename",2) Delete trigger: jrn("tablename",3) Code to write the changes on the journal table and to recover it on a different machine: **************************************************************************** **************************************************************************** *************************************************************************** *** Writes modifications en the journal table *************************************************************************** FUNCTION JRN LPARAMETER lctabla, lnaccion lcTabla = UPPER(ALLTRIM( lcTabla )) *** Global flag to stop writing journal table while recovering data IF VARTYPE(glstopjournal)="L" IF glstopjournal=.T. RETURN .T. ENDIF ENDIF LOCAL vcttxsdata(1, 1) LOCAL lcoldalias, lnOficina IF EMPTY(lcTabla) RETURN ENDIF IF !USED(lctabla) RETURN ENDIF lcoldalias = ALIAS() lcTabla = UPPER(ALLTRIM(lcTabla)) SELECT (lctabla) *** Number of fields on the table lnnumfields = AFIELDS(vctfields, lctabla) DIMENSION vcttxsdata(lnnumfields, 2) FOR i = 1 TO lnnumfields vcttxsdata(i, 1) = vctfields(i,1) && field name vcttxsdata(i, 2) = EVALUATE(vctfields(i,1)) && field value ENDFOR IF !USED("Journal") USE journal ALIAS journal IN 0 ENDIF lcmachinename = ALLTRIM(MachineName()) lcusername = ALLTRIM(UserName()) *** Writes on the journal INSERT INTO journal (fechahora, machine, USER, tabla, accion, pasado) VALUES (DATETIME(), lcmachinename, lcusername, lctabla, lnaccion, .F.) *** Writes on the memo field the vector SELECT journal SAVE TO MEMO txsdata ALL LIKE vcttxsdata *** saves modifications IF CURSORGETPROP("Buffering")=3 TABLEUPDATE(.T.,.T.) ENDIF *** HouseKeeping RELEASE vcttxsdata RELEASE vctfields *** Restaura la tabla en que estaba, por si acaso IF !EMPTY(lcoldalias) SELECT (lcoldalias) ENDIF ENDFUNC *************************************************************************** *** Recover data from the journal file *************************************************************************** PROCEDURE RecoverJournal LPARAMETER lcSyncDataFile PUBLIC glStopJournal glStopJournal = .T. IF !USED(lcSyncDataFile) USE &lcSyncDataFile IN 0 ALIAS SyncDataFile ENDIF SELECT SyncDataFile SCAN SELECT SyncDataFile = mergetx(UPPER(ALLTRIM(SyncDataFile.tabla)),SyncDataFile.accion,"SyncDataFile ") SELECT SyncDataFile ENDSCAN RELEASE glStopJournal ENDPROC * **************************************************************************** ********************************** *** Add, delete and modify data **************************************************************************** ********************************** PROCEDURE MergeTx LPARAMETER lcTabla, lnAccion, lcAliasJournal EXTERNAL ARRAY vcttxsdata LOCAL lnReturnValue LOCAL vctCampos(1) IF !USED(lcTabla) lcAlias = JUSTSTEM(lcTabla) USE (lcTabla) IN 0 ALIAS &lcAlias ELSE lcAlias = JUSTSTEM(lcTabla) ENDIF AFIELDS( vctCampos, lcalias ) DO CASE CASE lnAccion=1 && INSERT *** New record SELECT (lcAliasJournal) RESTORE FROM MEMO txsdata ADDITIVE IF VARTYPE(vcttxsdata)="U" RETURN ENDIF lnVctLen = ALEN(vcttxsdata, 1) SELECT (lcAlias) IF lookforrecord(lcTabla,@vcttxsdata) <> 0 && Record not found APPEND BLANK FOR i = 1 TO lnVctLen lcFieldName = UPPER(ALLTRIM(vcttxsdata(i,1))) IF ASCAN( vctCampos, lcFieldName ) > 0 REPLACE NEXT 1 &lcFieldName WITH vcttxsdata(i,2) ENDIF ENDFOR **** Guarda los cambios SELECT (lcAlias) IF CURSORGETPROP("Buffering")=3 TABLEUPDATE(.T.,.T.) ENDIF ENDIF CASE lnAccion=2 && UPDATE *** Modify SELECT (lcAliasJournal) RESTORE FROM MEMO txsdata ADDITIVE IF VARTYPE(vcttxsdata)="U" RETURN ENDIF lnVctLen = ALEN(vcttxsdata, 1) SELECT (lcAlias) && If the record exists modify it, if not creates it lnReturnValue = lookforrecord(lcTabla,@vcttxsdata) DO CASE CASE lnReturnValue = 0 && Record found SELECT (lcAlias) IF NOT EOF() FOR i = 1 TO lnVctLen lcFieldName = ALLTRIM(vcttxsdata(i,1)) lcFieldName = UPPER( ALLTRIM(lcFieldName)) && You can modify the code here to skip primary key fields IF ASCAN( vctCampos, lcFieldName ) > 0 REPLACE NEXT 1 &lcFieldName WITH vcttxsdata(i,2) ENDIF ENDFOR **** Save changes SELECT (lcAlias) IF CURSORGETPROP("Buffering")=3 TABLEUPDATE(.T.,.T.) ENDIF ENDIF CASE lnReturnValue = -1 && Record not found SELECT (lcAlias) APPEND BLANK FOR i = 1 TO lnVctLen lcFieldName = ALLTRIM(vcttxsdata(i,1)) IF ASCAN( vctCampos, lcFieldName ) > 0 REPLACE NEXT 1 &lcFieldName WITH vcttxsdata(i,2) ENDIF ENDFOR **** Guarda los cambios SELECT (lcAlias) IF CURSORGETPROP("Buffering")=3 TABLEUPDATE(.T.,.T.) ENDIF ENDCASE CASE lnAccion=3 && DELETE SELECT (lcAliasJournal) RESTORE FROM MEMO txsdata ADDITIVE IF VARTYPE(vcttxsdata)="U" RETURN ENDIF lnVctLen = ALEN(vcttxsdata, 1) SELECT (lcAlias) IF lookforrecord(lcTabla,@vcttxsdata)= 0 DELETE ENDIF **** Guarda los cambios SELECT (lcAlias) IF CURSORGETPROP("Buffering")=3 TABLEUPDATE(.T.,.T.) ENDIF ENDCASE RETURN ENDPROC * **************************************************************************** ********************* *** Looks for record... **************************************************************************** ********************* FUNCTION LookForRecord LPARAMETER lcTabla, vcttxsdata LOCAL lcstring, lnPosition, lcClaves, lnCounter, lnNumItems, lnLoop, lcNombreColumna, lcSyncTag LOCAL lnReferencia SET NEAR OFF && Evita que busque datos similares SET EXACT ON && Fuerza la coincidencia exacta en strings lcTabla = UPPER(ALLTRIM(lcTabla)) lnNumItems = ALEN(vcttxsdata, 1) lcSyncTag = "" lnOficina = 0 lnReferencia = 0 lcCompradCod = "" lndemanda = 0 FOR lnLoop = 1 TO lnNumItems lcNombreColumna = UPPER(ALLTRIM(vcttxsdata(lnLoop,1))) DO CASE CASE "SYNCTAG" $ UPPER(lcNombreColumna) lcSyncTag = vcttxsdata(lnLoop,2) CASE "OFICINA" $ UPPER(lcNombreColumna) lnOficina = vcttxsdata(lnLoop,2) CASE "REFERENCIA" $ UPPER(lcNombreColumna) lnReferencia = vcttxsdata(lnLoop,2) CASE "CODIGO" $ UPPER(lcNombreColumna) IF lcTabla = "COMPRAD" lcCompradCod = vcttxsdata(lnLoop,2) ENDIF CASE "DEMANDA" $ UPPER(lcNombreColumna) IF lcTabla = "DEMANDA" lnDemanda = vcttxsdata(lnLoop,2) ENDIF ENDCASE ENDFOR *** Empty Synctag = ERROR IF EMPTY(lcSyncTag) RETURN -3 ENDIF *** Looks for the key SELECT (lcTabla) SET ORDER TO TAG SyncTag IF SEEK( lcSyncTag ) RETURN 0 ELSE RETURN -1 ENDIF ENDFUNC * FUNCTION dato2string LPARAMETER loDato LOCAL lcReturnString lcReturnString = "" DO CASE CASE VARTYPE(loDato)="C" lcReturnString = CHR(34)+loDato+CHR(34) CASE VARTYPE(loDato)="N" lcReturnString = ALLTRIM(STR(loDato)) CASE VARTYPE(loDato)="Y" lcReturnString = ALLTRIM(STR(loDato)) CASE VARTYPE(loDato)="L" lcReturnString = IIF(loDato, ".T.", ".F.") CASE VARTYPE(loDato)="O" CASE VARTYPE(loDato)="G" CASE VARTYPE(loDato)="D" lcReturnString = "CTOD("+DTOC(loDato)+")" CASE VARTYPE(loDato)="T" lcReturnString = "CTOT("+TTOC(loDato)+")" CASE VARTYPE(loDato)="X" CASE VARTYPE(loDato)="U" ENDCASE RETURN lcReturnString ENDFUNC * _______________________________________________ Post Messages to: ProFox@leafe.com Subscription Maintenance: http://leafe.com/mailman/listinfo/profox OT-free version of this list: http://leafe.com/mailman/listinfo/profoxtech Searchable Archive: http://leafe.com/archives/search/profox This message: http://leafe.com/archives/byMID/profox/0B08A57A16F3435BBD2B7B27F11C0DF2@LENOVO1 ** All postings, unless explicitly stated otherwise, are the opinions of the author, and do not constitute legal or medical advice. This statement is added to the messages for those lawyers who are too stupid to see the obvious.