Music Systems – Welcome

This content is password-protected. To view it, please enter the password below.

FORM frmPilgrims - Tab 2 (Pilgrim data) VFP Method Code

Introduction
This code manages…

frmPilgrims.NewPilgrims
When the op selects a pilgrim row from the selection grid on Tab 1, this method manages setting up the tabs/pages so that the data from any previously-processed pilgrim is cleared out

*– prime the properties with fresh pilgrim fields from SQL Server
PROCEDURE newpilgrims
LPARAMETERS tlReset
* —————————————————————————
*
*  thisform.NewPilgrims() method. Called from thisform.pgfpageframe1.grdnav.selectionmade()
*    also called from thisform.pgfpageframe1.ctrSelPilgrims1.cmdApply.Click if only
*    one record comes from the server based on the selection criteria.
*  20mar09 we now also call this from the cancelpilgrims() method as part of the
*    procedure to restore the pilgrim information after a Cancel button click.
*
* —————————————————————————
LOCAL LOK
 
IF PARAMETERS() < 1
   tlReset = .F.
ENDIF
 
*
*  since Pilgrim is new, the form’s retained IDs of child and grandchild
*    IDs are all now obsolete
*
thisform.nVTID = 0
thisform.nBKID = 0
 
*
*  (added these resets 16jul07). when pilgrim changes, all these values become
*    obsolete. note that when visit changes, booking becomes obsolete, so we
*    need to put a reduced version of this code into newvisits. also a further
*    reduced on into newbookings.
*
STORE 0 TO thisform.nPage3PMID, ;
   thisform.nPage4PMID, thisform.nPage4VTID, ;
   thisform.nPage5PMID, thisform.nPage5VTID, thisform.nPage5BKID
 
*
*  test for option to clear out properties. we may never need this FOR PILGRIMS,
*    but we certainly will need it for Visits and Bookings when the user clicks the
*    Clear button on page 1.
*
IF tlReset
   *
   *  Indicate no Pilgrim is selected yet
   *
   thisform.nPMID = 0
 
   *
   *  Empty the cursor to clear the navigation grid
   *
   SELECT curPilgrims
   this.clearcursor()
   
   *
   *  make blank-looking buttons since there’s no info behind them at this point.
   *
   STORE this.BackColor TO this.cmdHousekeeping.BackColor, this.cmdMedical.backcolor
   STORE RGB(64,64,64) TO this.cmdHousekeeping.foreColor, this.cmdMedical.forecolor
   this.nPilgrimage = 0
 
   this.Refresh()
   RETURN .T.
ENDIF
 
*
*  manage the calculated Age field and the colors of the command buttons
*
this.refreshpilgrims()
 
*
*  experimental code added 15dec07. If we’re doing an Add Pilgrim operation, this
*    method would be finished, because it would be called from the Add Pilgrim
*    command button with the tlRest parameter set to .T.
*  by falling through here we know we have come from Selectionmade() of the grid
*    and we have an existing pilgrim to set up for.
*  The experiment is to put this pilgrim’s data into the carry-forward parameter
*    block. So before you add a new pilgrim, if you simply retrieve (not add)
*    a relative, you get all the address data in the block and your right-click
*    of Add Pilgrim then will let you bring forward a lot of data.
*
LOCAL loBlock
IF TYPE(“thisform.oPMparamblock”) = “O”
   loBlock = thisform.oPMparamblock
   lnRows = ALEN(loBlock.aParameters,1)
   FOR ln = 1 TO lnRows
      lcField = loBlock.aParameters(ln,PB_FIELD_NAME)
      luValue = &lcField
      loBlock.aParameters(ln,PB_FIELD_VALUE) = luValue   &&  may be empty–that’s ok
   ENDFOR
ENDIF
 
RETURN .T.
ENDPROC

…Page2-cntDEpilgrims.SavePilgrims
Op clicks Save -> SaveAction method -> Save Pilgrims (this)
it manages the save only to hPilgrims.

*– called from thisform.saveaction() method to do the work of saving a new or edited Pilgrims rcd
PROCEDURE savepilgrims
LPARAMETERS toBlock
*
* the form’s saveaction method first calls this method to save Pilgrim info,
*    then any Visits, and finally any Bookings. That order is determined by
*    the possibilty that we added a Pilgrims row, in which case we need the ID
*    to put into hVisits, and/or we added a Visits row in which case we need
*    the new Visits ID for the Bookings.
*
*  4June06 added the parameter in support of the new carry forward feature
 
PRIVATE nIDvalue
LOCAL lcName,lcDatetime,lcTemp
 
 
*
*  get the handle first because we know a row needs saving (because we were called by form’s
*    saveaction method based on testing thisform.lModpilgrims)
*
lnHandle = oLib.oConnectionSVC.GetSingleConnectHandle()
IF lnHandle <= 0
   =MESSAGEBOX(“Unable to establish contact with the SQL Server computer”,MBEO,”Connectivity Problem”)
   RETURN .F.
ENDIF
 
*
*  24jan15 new code added because SQL Server will bomb if we try to add a row with no gender.
*    He’re we’ll handle that situation more gracefully by detecting it in advance. IF WE EVER
*    need to allow a missing Gender (e.g. for members of a family staying in some new kind
*    of family quarters, we’ll need to modify (at least) the hPilgrims insert trigger.
*
IF ATC(curPilgrims.PM_cGender,”MF”) < 1
   =MESSAGEBOX(“Pilgrim’s gender must be set to ‘M’ or ‘F’ or we can’t do the save.”,MBEO,”Gender required”)
   RETURN .F.
ENDIF
 
*
*  defend ourselves against a SQL Server crash caused by single quote, unpaired, in any character
*   property
*
REPLACE ;
   PM_cFirst    WITH thisform.fixquote(curPilgrims.PM_cFirst), ;
   PM_cMiddle   WITH thisform.fixquote(curPilgrims.PM_cMiddle), ;
   PM_cLast     WITH thisform.fixquote(curPilgrims.PM_cLast), ;
   PM_cNicknm   WITH thisform.fixquote(curPilgrims.PM_cNicknm), ;
   PM_cAddr1    WITH thisform.fixquote(curPilgrims.PM_cAddr1), ;
   PM_cAddr2    WITH thisform.fixquote(curPilgrims.PM_cAddr2), ;
   PM_cAddr3    WITH thisform.fixquote(curPilgrims.PM_cAddr3), ;
   PM_cCity     WITH thisform.fixquote(curPilgrims.PM_cCity), ;
   PM_cState    WITH thisform.fixquote(curPilgrims.PM_cState), ;
   PM_cZcode    WITH thisform.fixquote(curPilgrims.PM_cZcode), ;
   PM_cCountry  WITH thisform.fixquote(curPilgrims.PM_cCountry), ;
   PM_cPhone    WITH thisform.fixquote(curPilgrims.PM_cPhone), ;
   PM_cPhone2   WITH thisform.fixquote(curPilgrims.PM_cPhone2), ;
   PM_cCell     WITH thisform.fixquote(curPilgrims.PM_cCell), ;
   PM_cEmail    WITH thisform.fixquote(curPilgrims.PM_cEmail), ;
   PM_mNotes    WITH thisform.fixquote(curPilgrims.PM_mNotes), ;
   PM_mMednotes WITH thisform.fixquote(curPilgrims.PM_mMednotes), ;
   PM_mHKnotes  WITH thisform.fixquote(curPilgrims.PM_mHKnotes), ;
   PM_mRegnotes WITH thisform.fixquote(curPilgrims.PM_mRegnotes), ;
   PM_mRelated  WITH thisform.fixquote(curPilgrims.PM_mRelated), ;
   PM_mOccup    WITH thisform.fixquote(curPilgrims.PM_mOccup) IN curPilgrims
 
*
*  13dec18 new mods to include userid and date in the parameters. there are
*    corresponding mods in the stored procedures and triggers for hPilgrims.
*
lcName = IIF(TYPE(“oApp.cUsername”) = “C”, ;
   oApp.cUsername, “Unknown/Unknown”)
 
*
*  first convert today’s date to char, like ‘2458501’, then convert
*    to the form ’12/25/2018′, then put this in SQL Server CASTable
*    form as ‘2018.12.25’
*  25dec18 modified to include time as well as date in SQL server calls
*
SET CENTURY ON
lcTemp = DTOC(DATETIME())
lcDatetime = SUBSTR(lcTemp,7,4)+’-‘+SUBSTR(lcTemp,4,2)+’-‘+LEFT(lcTemp,2)+’ ‘+TIME()
 
*
*  are we doing an add or an edit? pcMode won’t help. we look at the id number. If an add
*    there’s no primary key assigned yet by SQL Server. We assigned a temporary key value of -1.
*
DO CASE
CASE thisform.nPMID < 0
   *
   *  manage missing birthdate. If the date is truly not given below, the SP will
   *    reject the INSERT. So here we dummy up a missing date by using the constant 
   *    01/01/1900. (Added this code 09Oct06 as partial response to Irene’s complaint
   *    that New Pilgrims with missing gender and date of birth are rejected. Still
   *    don’t want to allow bad or incomplete data to be entered. How can we accept someone
   *    of no known age and gender? Where to book him/her/it?)
   *
   IF EMPTY(curPilgrims.PM_dBirth)
      replace PM_dBirth WITH DATE(1900,01,01) IN curPilgrims
   ENDIF
 
   *
   *  new 15jan15. here we protect against the VALID of the prompt for PM_lCform not having
   *    fired. Initially on a new pilgrim Add, this is set to -1. If on coming here it’s still -1, then
   *    we’ll derive the likely value here because we can’t tolerate anything but a 0 or a 1 in the field.
   *
   IF curPilgrims.PM_lCform = -1
      replace PM_lCform WITH IIF(UPPER(ALLTRIM(curPilgrims.PM_cCountry)) = ‘INDIA’,0,1) IN curPilgrims
   ENDIF
 
   * —————————————————————–
   *
   *  new code 11July07. Here we check for an existing record with same
   *    name as the one we are adding, and warn the user if it exists.
   *
   * —————————————————————–
   *
   *  prep parameter list for the Retrieve SP
   *
   nIDvalue = 0
   lcExpression = “”
   IF !EMPTY(curPilgrims.PM_cFirst)
      IF !EMPTY(lcExpression)
         lcExpression = lcExpression + “, “
      ENDIF
      lcExpression = lcExpression + “@tFirst='”+strtran(TRIM(curPilgrims.PM_cFirst),”‘”,””””)+”‘”
   ENDIF 
   IF !EMPTY(curPilgrims.PM_cLast)
      IF !EMPTY(lcExpression)
         lcExpression = lcExpression + “, “
      ENDIF
      lcExpression = lcExpression + “@tLast='”+strtran(TRIM(curPilgrims.PM_cLast),”‘”,””””)+”‘”
   ENDIF 
   IF !EMPTY(curPilgrims.PM_cMiddle)
      IF !EMPTY(lcExpression)
         lcExpression = lcExpression + “, “
      ENDIF
      lcExpression = lcExpression + “@tMiddle='”+strtran(TRIM(curPilgrims.PM_cMiddle),”‘”,””””)+”‘”
   ENDIF 
   lcExpression = lcExpression + “, @tScope=3 “
 
   *
   *  do the lookup
   *
   lcCmd = “EXEC dbo.USP_Ret_Pilgrims “+lcExpression
   lnResult = H2SQLexe(lnHandle, lcCmd, “RS_Dup”)
   IF lnResult > 0 AND USED(“RS_Dup”) AND RECCOUNT(“RS_Dup”) >= 1
      *
      *  have a dup record, maybe even 2+!
      *
      lcMessage = “Found one or more Pilgrim record(s) with matching name:”+CR2
         SELECT RS_Dup
         SCAN
            lcMessage = lcMessage + TRIM(RS_Dup.PM_cFirst) + TRIM(” “+RS_Dup.PM_cMiddle) + TRIM(” “+RS_Dup.PM_cLast)+CR+ ;
               TRIM(RS_Dup.PM_cCity)+”, “+TRIM(RS_Dup.PM_cState)+”, “+TRIM(RS_Dup.PM_cCountry)+CR
            IF !EMPTY(RS_Dup.PM_dBirth) AND RS_Dup.PM_dBirth != DATE(1900,01,01)
               lcTemp = DTOS(RS_Dup.PM_dBirth)
               lcMessage = lcMessage + “Born “+LEFT(lcTemp,4)+”/”+SUBSTR(lcTemp,5,2)+”/”+SUBSTR(lcTemp,7)+CR2
            ELSE
               lcMessage = lcMessage + “(Unknown birth date)”+CR2
            ENDIF 
         ENDSCAN
      IF IDNO = MESSAGEBOX(lcMessage+”Yes–Go ahead and ADD this pilgrim anyway (not a duplicate)”+CR+”No–Don’t add, it really is a duplicate”,MBQYN,”Apparent Duplicate Pilgrim!”)
         USE IN RS_Dup
         RETURN .F.
      ENDIF
   ENDIF
   IF USED(“RS_Dup”)
      USE IN RS_Dup
   ENDIF
   
 
   *
   *  prep parameter list for the Insert SP
   *
   *  06jan19 modified this a little to put an ‘*’ prefix on PM_cIDupdate, which reassures 
   *    the UPDATE trigger that we have already supplied ID and date. No need for it
   *    to overwrite these from the trigger. Benefit is that in Windows we can determine the 
   *    actual Win login ID, whereas Azure can only see the ID used in the connection string.
   *
   lcExpression = “?@nIDvalue, “+ ;
“@PM_cFirst='”+TRIM(curPilgrims.PM_cFirst)+”‘, “+ ;
“@PM_cMiddle='”+TRIM(curPilgrims.PM_cMiddle)+”‘, “+ ;
“@PM_cLast='”+TRIM(curPilgrims.PM_cLast)+”‘, “+ ;
“@PM_cNicknm='”+TRIM(curPilgrims.PM_cNicknm)+”‘, “+ ;
“@PM_cAddr1='”+TRIM(curPilgrims.PM_cAddr1)+”‘, “+ ;
“@PM_cAddr2='”+TRIM(curPilgrims.PM_cAddr2)+”‘, “+ ;
“@PM_cAddr3='”+TRIM(curPilgrims.PM_cAddr3)+”‘, “+ ;
“@PM_cCity='”+TRIM(curPilgrims.PM_cCity)+”‘, “+ ;
“@PM_cState='”+TRIM(curPilgrims.PM_cState)+”‘, “+ ;
“@PM_cZCode='”+TRIM(curPilgrims.PM_cZCode)+”‘, “+ ;
“@PM_cCountry='”+TRIM(curPilgrims.PM_cCountry)+”‘, “+ ;
“@PM_cGender='”+TRIM(curPilgrims.PM_cGender)+”‘, “+ ;
“@PM_dBirth=”+IIF(!EMPTY(curPilgrims.PM_dBirth),”‘”+STUFF(STUFF(DTOS(curPilgrims.PM_dBirth),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
“@PM_cPhone='”+TRIM(curPilgrims.PM_cPhone)+”‘, “+ ;
“@PM_cPhone2='”+TRIM(curPilgrims.PM_cPhone2)+”‘, “+ ;
“@PM_cCell='”+TRIM(curPilgrims.PM_cCell)+”‘, “+ ;
“@PM_cEmail='”+TRIM(curPilgrims.PM_cEmail)+”‘, “+ ;
“@PM_mNotes='”+TRIM(LEFT(curPilgrims.PM_mNotes,250))+”‘, “+ ;
“@PM_mMednotes='”+TRIM(LEFT(curPilgrims.PM_mMednotes,250))+”‘, “+ ;
“@PM_mHKnotes='”+TRIM(LEFT(curPilgrims.PM_mHKnotes,250))+”‘, “+ ;
“@PM_mRegnotes='”+TRIM(LEFT(curPilgrims.PM_mRegnotes,250))+”‘, “+ ;
“@PM_mRelated='”+TRIM(LEFT(curPilgrims.PM_mRelated,250))+”‘, “+ ;
“@PM_mOccup='”+TRIM(LEFT(curPilgrims.PM_mOccup,250))+”‘, “+ ;
“@PM_lCform='”+STR(curPilgrims.PM_lCform,1)+”‘, “+ ;
    “@PM_cIDCreate='”+lcName+”‘, “+ ;
    “@PM_dCreate='”+lcDatetime+”‘, “+ ;
    “@PM_cIDupdate=’*”+lcName+”‘, “+ ;
    “@PM_dUpdate='”+lcDatetime+”‘”
 
 
   *
   *  insert the row
   *
   lcCmd = “EXEC dbo.USP_Pilgrims_Insert “+lcExpression
   lnResult = H2SQLexe(lnHandle, lcCmd)
 
   IF nIDvalue > 0
      *
      *  insert worked, so retain the real SQL Server-assigned primary key. 
      *
      thisform.nPMID = nIDvalue
      
      *
      *  essential that we update our cursor with the SQLassigned new ID.
      *    Plan B would be to call findpilgrims() and let it re-retrieve the
      *    result set and rebuild curPilgrims, as we do (only) with ctrDEvisits,
      *    (not ctrDEBookings or ctrDERoom)
      *
      replace PM_ID WITH nIDvalue IN curPilgrims
   ELSE
      *  error handling needed here!
      =MESSAGEBOX(“Unable to insert a new Pilgrims record in the database”,MBEO,”Data Problem”)
      RETURN .F.
   ENDIF
   
   *
   *  now retain the latest field values in the carry forward parameter block
   *
   this.savecarry(toBlock)
 
CASE thisform.nPMID > 0
   *
   *  prep the Update param list. Note that the SP logic (at least as first conceived)
   *    requires that we send all fields back, whether they were modified or not.
   *    The Update trigger will determine which fields (if any) were changed,
   *    spin off a hChanges row, and update the timestamp and ID of the Pilgrims row.
   *
   *  06jan19 added the * prefix on PM_cIDupdate, see above.
   *
      PRIVATE nResult
      nResult = 0
      lcExpression = “?@nResult, “+ ;
“@PM_ID=”+LTRIM(STR(thisform.nPMID))+”, “+ ;
“@PM_cFirst='”+TRIM(curPilgrims.PM_cFirst)+”‘, “+ ;
“@PM_cMiddle='”+TRIM(curPilgrims.PM_cMiddle)+”‘, “+ ;
“@PM_cLast='”+TRIM(curPilgrims.PM_cLast)+”‘, “+ ;
“@PM_cNicknm='”+TRIM(curPilgrims.PM_cNicknm)+”‘, “+ ;
“@PM_cAddr1='”+TRIM(curPilgrims.PM_cAddr1)+”‘, “+ ;
“@PM_cAddr2='”+TRIM(curPilgrims.PM_cAddr2)+”‘, “+ ;
“@PM_cAddr3='”+TRIM(curPilgrims.PM_cAddr3)+”‘, “+ ;
“@PM_cCity='”+TRIM(curPilgrims.PM_cCity)+”‘, “+ ;
“@PM_cState='”+TRIM(curPilgrims.PM_cState)+”‘, “+ ;
“@PM_cZCode='”+TRIM(curPilgrims.PM_cZCode)+”‘, “+ ;
“@PM_cCountry='”+TRIM(curPilgrims.PM_cCountry)+”‘, “+ ;
“@PM_cGender='”+TRIM(curPilgrims.PM_cGender)+”‘, “+ ;
“@PM_dBirth=”+IIF(!EMPTY(curPilgrims.PM_dBirth),”‘”+STUFF(STUFF(DTOS(curPilgrims.PM_dBirth),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
“@PM_cPhone='”+TRIM(curPilgrims.PM_cPhone)+”‘, “+ ;
“@PM_cPhone2='”+TRIM(curPilgrims.PM_cPhone2)+”‘, “+ ;
“@PM_cCell='”+TRIM(curPilgrims.PM_cCell)+”‘, “+ ;
“@PM_cEmail='”+TRIM(curPilgrims.PM_cEmail)+”‘, “+ ;
“@PM_mNotes='”+TRIM(LEFT(curPilgrims.PM_mNotes,250))+”‘, “+ ;
“@PM_mMednotes='”+TRIM(LEFT(curPilgrims.PM_mMednotes,250))+”‘, “+ ;
“@PM_mHKnotes='”+TRIM(LEFT(curPilgrims.PM_mHKnotes,250))+”‘, “+ ;
“@PM_mRegnotes='”+TRIM(LEFT(curPilgrims.PM_mRegnotes,250))+”‘, “+ ;
“@PM_mRelated='”+TRIM(LEFT(curPilgrims.PM_mRelated,250))+”‘, “+ ;
“@PM_mOccup='”+TRIM(LEFT(curPilgrims.PM_mOccup,250))+”‘, “+ ;
“@PM_lCform='”+STR(curPilgrims.PM_lCform,1)+”‘, “+ ;
    “@PM_cIDUpdate=’*”+lcName+”‘, “+ ;
    “@PM_dUpdate='”+lcDatetime+”‘”
 
   *
   *  update the row
   *
   lcCmd = “EXEC dbo.USP_Pilgrims_Update “+lcExpression
   lnResult = H2SQLexe(lnHandle, lcCmd)
   IF lnResult <= 0 OR nResult < 0
      *  error handling needed here!
   ENDIF
   
   *
   *  now retain the latest field values in the carry forward parameter block
   *
   this.savecarry(toBlock)
ENDCASE
 
RETURN .T.
ENDPROC

…Page2.cntDEpilgrims.RefreshPilgrims
If the op changes one or more fields on the form, then clicks Cancel. This refreshes the screen with the original values, thus implementing the Cancel.

*– needed when user has altered properties, then clicked Cancel. We restore the current settings.
PROCEDURE refreshpilgrims
 
*
*  new regime–no more properties. We show the curPilgrims selected row in the
*    page 2 fields. So all we need to do here is manage the buttons on page 2
*    that change color if the memo field contents are present.
*
 
IF EMPTY(curPilgrims.PM_mMednotes)
   *
   *  blend-in colors to indicate no data present
   *
   STORE this.BackColor TO this.cmdMedical.backcolor
   STORE RGB(64,64,64) TO this.cmdMedical.forecolor
ELSE
   *
   *  kind of orange ‘alert’ that data exists
   *
   STORE EVALUATE(this.nHighlight) TO this.cmdMedical.backcolor
   STORE RGB(0,0,0) TO this.cmdMedical.forecolor
ENDIF
 
*  same deal for this button…
IF EMPTY(curPilgrims.PM_mHKnotes)
   STORE this.BackColor TO this.cmdHousekeeping.BackColor
   STORE RGB(64,64,64) TO this.cmdHousekeeping.foreColor
ELSE
   STORE EVALUATE(this.nHighlight) TO this.cmdHousekeeping.BackColor
   STORE RGB(0,0,0) TO this.cmdHousekeeping.foreColor
ENDIF
 
*  same deal for this button…
IF EMPTY(curPilgrims.PM_mRegnotes)
   STORE this.BackColor TO this.cmdRegnotes.BackColor
   STORE RGB(64,64,64) TO this.cmdRegnotes.foreColor
ELSE
   STORE EVALUATE(this.nHighlight) TO this.cmdRegnotes.BackColor
   STORE RGB(0,0,0) TO this.cmdRegnotes.foreColor
ENDIF
 
*
*  new special setup processing for the lCform flag. Added 15dec14
*
IF (curPilgrims.PM_lCform=0 AND UPPER(curPilgrims.PM_cCountry) != “INDIA”) OR ;
   (curPilgrims.PM_lCform=1 AND UPPER(curPilgrims.PM_cCountry) = “INDIA”)
   *
   *  colorize an unusual case as an exception. even though this pilgrim has an Indian addr
   *    s/he needs a C Form for the police. e.g. person is a PIO, not an Indian citizen, but residing in India.
   *
   STORE EVALUATE(this.nHighlight) TO this.chkCform.BackColor
   ***STORE RGB(0,0,0) TO this.chkCform.foreColor
ELSE
   *
   *  normal colors for a normal case. either an Indian who does not need a C form or a foreigner
   *    who does need a C form.
   *
   STORE this.BackColor TO this.chkCform.BackColor
   ***STORE RGB(64,64,64) TO this.chkCform.foreColor
ENDIF
 
 
*
*  we display the age BUT WE DON’T SAVE IT in the table any more (for good reason)
*
ldBirth = curPilgrims.PM_dBirth
ldNow = DATE()
IF ldBirth > ldNow
   ldBirth = DATE(YEAR(ldBirth)-100,MONTH(ldBirth),DAY(ldBirth))
   replace PM_dBirth WITH ldBirth IN curPilgrims
ENDIF
DO CASE
CASE ldBirth = DATE(1900,01,01)        &&  this is the birthdate we record when birthdate is missing
   this.nPilgrimage = 0
CASE !EMPTY(ldBirth) AND TYPE(“ldBirth”)=”D”
   this.nPilgrimage = LTRIM(Str(YEAR(ldNow)-YEAR(ldBirth) ;
      – IIF(RIGHT(DTOS(ldBirth),4) > RIGHT(DTOS(ldNow),4),1,0)))
OTHE
   this.nPilgrimage = 0
ENDCASE
this.txtAge.Refresh()
 
RETURN .T.
ENDPROC

…Page2.cntDEpilgrims.SaveCarry
There is a SaveCarry method on each of Tabs 2,3,4. After a successful Save, this method retains some of the data from the Saved row that can be pre-filled into the fields of the new Add Pilgrim row IF and only IF op Right Clicks the Add Pilgrim button.

*– during SavePilgrims, we call this method to retain altered values in the parameter block passed by the form
PROCEDURE savecarry
LPARAMETERS toBlock
LOCAL ln, lnrows, lcField
 
*
*  if the user has invoked the ‘carry’ options, we save the demographic fields in
*    a parameter block just after they have been written.
*
lnRows = ALEN(toBlock.aParameters,1)
FOR ln = 1 TO lnRows
   lcField = toBlock.aParameters(ln,PB_FIELD_NAME)
   luValue = &lcField
   toBlock.aParameters(ln,PB_FIELD_VALUE) = luValue   &&  may be empty–that’s ok
ENDFOR
 
RETURN .t.
ENDPROC

…Page2.cntDEpilgrims.ClearCarry
Once op does an Add Pilgrim procedure without Right Clciking the Add Pilgrim button we clear out the retained address and contact into using this method. There is similar logic in a method of the same name on Tabs 3 and 4 as well.

*– clear the carry forward parameter block retained data when a LEFT click of Add Pilgrim is received.
PROCEDURE clearcarry
lparameters toBlock
 
*
*  need to clear the carry forward data for pilgrims, since the new pilgrim is NOT using
*    carry forward and we don’t want the last guy’s data to clobber what we are about
*    to enter when the user comes to this.savecarry()
*
lnRows = ALEN(toBlock.aParameters,1)
FOR ln = 1 TO lnRows
   lcField = toBlock.aParameters(ln,PB_FIELD_NAME)
   lcType = TYPE(lcField)
   *
   *  note–pretty sure all field values are Character…
   *
   DO CASE
   CASE lcType = “C”
      STORE “” TO toBlock.aParameters(ln,PB_FIELD_VALUE)
   CASE lcType = “N”
      STORE 0 TO toBlock.aParameters(ln,PB_FIELD_VALUE)
   CASE lcType = “L”
      STORE .F. TO toBlock.aParameters(ln,PB_FIELD_VALUE)
   ENDCASE
ENDFOR
 
RETURN .T.
ENDPROC

…Page2.cntDEpilgrims.CancelPilgrims
This cleans up after a failed Add or Edit (a Save). Extremely rare that this will be run, but that’s programming–cover all the possibilities.

*– handle failed add or edit in the most graceful way
PROCEDURE cancelpilgrims
 
*
*  handle an aborted Add. two reasons are possible: no name/gender, or duplicate record exists
*
IF thisform.nPMID < 0
   thisform.nPMID=0
   thisform.lModpilgrims = .F.
   this.clearcursor()
   thisform.pgfPagerefresh1.Activepage = 1
   WAIT WINDOW “Pilgrim not added” timeout(0.7)
ELSE
   *
   *  20mar09 musings about a canceled edit. When the user alters a curPilgrims row
   *    and then clicks Cancel, there was no code here to restore the curPilgrims 
   *    fields to their former value. The pilgrim looks modified (though it isn’t).
   *  in the corresponding cancel(visits, bookings, room) methods we’d call the
   *    find(visits,bookings,room) methods to rebuild the cursor, so the Cancel
   *    effect would be visible.
   *  for pilgrims, however, there is no findpilgrims() method in this container,
   *    so restoring the pilgrim row will not follow the same path. This issue came up
   *    when demoing Medical Needs notes data entry to Pat. You add medical notes,
   *    then click OK. The Save/Cancel butons appear. You choose Cancel, but you find
   *    that the notes still seem to be there.
   *
   lnid = curPilgrims.PM_ID        &&  remember what row we’re working on
   *
   *  re-run the retrieve that was used originally by the Apply button on page 1.
   *    It was saved by the original call to retrievepilgrims()
   *
   this.retrievepilgrims(this.cRetrieveExpression)
   SELECT curPilgrims
   LOCATE FOR PM_ID = lnid
   this.newpilgrims()
   
ENDIF
 
RETURN .T.
ENDPROC
 

…Page2.cntDEpilgrims.ClearCursor
This is called for each Tab 2 through 5 when a new pilgrim has been selected by the op.

PROCEDURE clearcursor
LOCAL lnSel, ln, lok
 
lnSel = SELECT()
SELECT CurPilgrims
COUNT FOR !DELETED() TO ln
IF ln > 0
   lok = .t.
   TRY 
      ZAP           &&  ZAP of our cursor works, but DELETE FROM below was leaving a row behind
   CATCH            &&    some of the time(!)
      lok = .F.
   ENDTRY
   IF !lok
      DELETE FROM curPilgrims WHERE .T.
   ENDIF
ENDIF
 
SELECT (lnSel)
RETURN (ln > 0)
ENDPROC

…Page2.cntDEpilgrims.RetrievePilgrims
The op fills in some selection fields on page 1 and clicks Apply. Apply.Click calls this method to do the actual search for one or more pilgrims fitting the given specs.

*– code from the page1 apply command button. now shared with txtMiddle.Customvalid, which gives new way to select a pilgrim
PROCEDURE retrievepilgrims
LPARAMETERS tcExpression
 
*
*  retain the caller’s expression so we can recycle it when calling this method
*    from calcelpilgrims()
*
this.cRetrieveExpression = tcExpression
 
*
*  make sure grdnav’s record source is set and ready to be appended to
*
SELECT curPilgrims
IF RECCOUNT(“curPilgrims”) > 0
   lok = .T.
   TRY
   ZAP
   CATCH
      lok = .F.
   ENDTRY
   IF !lok
      DELETE FROM curPilgrims WHERE .T.
   ENDIF
ENDIF
 
*
*  get connect handle
*
lnHandle = oLib.oConnectionSVC.GetSingleConnectHandle()
IF lnHandle <= 0
   =MESSAGEBOX(“Unable to connect to SQL Server”,MBEO,”Communication Problem”)
   RETURN .T.
ENDIF
 
*
*  ok, time to do the retrieve
*
lcCmd = “EXEC USP_Ret_Pilgrims “+tcExpression
_cliptext = lcCmd
lnResult = H2SQLexe(lnHandle, lcCmd, “RS_Pilgrims”)
IF lnResult <= 0
   *
   *  error message will have been reported by h2Sqlexe
   *
   RETURN .T.
ENDIF
IF !USED(“RS_Pilgrims”)
   RETURN .T.
ENDIF
IF RECCOUNT(“RS_Pilgrims”) < 1
   WAIT WINDOW “No matching Pilgrims found” timeout(1)
   USE IN RS_Pilgrims
   RETURN .T.
ENDIF
 
*
*  install result set rows in our cursor for display
*
SELECT RS_Pilgrims
lcDBF = DBF()
*
*  defend against null birthdate. Keep this code unless/until birthdt
*    becomes a required field.
*
REPLACE ALL PM_dBirth WITH {//} FOR ISNULL(PM_dBirth)
*  added support for this newly-added date 16Nov07. often this will be unknown, 
*    depending on the scope, and we return 1/1/1900 as the date when the date can not
*    be known. this prevents NULL conversion problems in VFP.
*
REPLACE ALL VT_dUltstart WITH {//} FOR TTOD(VT_dUltstart)=DATE(1900,1,1)   &&  added 16Nov07
 
SELECT curPilgrims
APPEND FROM &lcDBF   &&  insert rows, rebuild indexes
GOTO TOP
USE IN RS_Pilgrims
 
RETURN .T.
ENDPROC

…Page2.cntDEpilgrims.DuplicateCheck
I think this method is obsolete. Can’t find any code that call it any more…Safe to ignore.

PROCEDURE duplicatecheck
* —————————————————————–
*
*  08Jan08 cloned this code from savepilgrims() so that we can do early
*    detection of a duplicate pilgrim.  We check for an existing record
*    with same name as the one we are adding.
*    Extra sophistication will allow us to convert an Add into
*    an Edit if a duplicate is identified and the user accepts one of the
*    duplicate records as the desired one.
*
* —————————————————————–
LOCAL tnID, lcExpression, lnHandle, lnResult, lcCmd, lnDups, lcMessage, ldNow, ;
   lcPilgrimage, ldBirth
 
WAIT WINDOW “Checking for duplicate records” timeout(0.8)
 
*
*  prep parameter list for the Retrieve SP
*
lcExpression = “”
IF !EMPTY(curPilgrims.PM_cFirst)
   IF !EMPTY(lcExpression)
      lcExpression = lcExpression + “, “
   ENDIF
   lcExpression = lcExpression + “@tFirst='”+strtran(TRIM(curPilgrims.PM_cFirst),”‘”,””””)+”‘”
ENDIF 
IF !EMPTY(curPilgrims.PM_cLast)
   IF !EMPTY(lcExpression)
      lcExpression = lcExpression + “, “
   ENDIF
   lcExpression = lcExpression + “@tLast='”+strtran(TRIM(curPilgrims.PM_cLast),”‘”,””””)+”‘”
ENDIF 
IF !EMPTY(curPilgrims.PM_cMiddle)
   IF !EMPTY(lcExpression)
      lcExpression = lcExpression + “, “
   ENDIF
   lcExpression = lcExpression + “@tMiddle='”+strtran(TRIM(curPilgrims.PM_cMiddle),”‘”,””””)+”‘”
ENDIF 
lcExpression = lcExpression + “, @tScope=3 “
 
*
*  get connection handle. Actually H2SQLexe would do that for us…
*
LOCAL lnHandle, lcCmd, lnResult, lcFilenm
lnHandle     = oLib.oConnectionSVC.GetSingleConnectHandle()
IF lnHandle <= 0
   RETURN .F.
ENDIF
 
*
*  do the lookup
*
lcCmd = “EXEC dbo.USP_Ret_Pilgrims “+lcExpression
lnResult = H2SQLexe(lnHandle, lcCmd, “RS_Dup”)
DIMENSION laDups(1,2)
lnDups = 0
IF lnResult > 0 AND USED(“RS_Dup”) AND RECCOUNT(“RS_Dup”) >= 1
      
   lcMessage = ”
   ldNow = DATE()
   SELECT RS_Dup
   SCAN
      *
      *  have a dup record, maybe even 2+!
      *
      lnDups = lnDups + 1
      DIMENSION laDups(lnDups,2)
 
      *
      *  calc age of possible dup pilgrim
      *
      ldBirth = TTOD(PM_dBirth)
      IF ldBirth > ldNow
         ldBirth = DATE(YEAR(ldBirth)-100,MONTH(ldBirth),DAY(ldBirth))
      ENDIF
 
      *
      *  make birthdate into display format
      *
      DO CASE
      CASE ldBirth = DATE(1900,01,01)        &&  this is the birthdate we record when birthdate is missing
         lcPilgrimage = “Unknown”
      CASE !EMPTY(ldBirth) AND TYPE(“ldBirth”)=”D”
         lcPilgrimage = LTRIM(Str(YEAR(ldNow)-YEAR(ldBirth) ;
            – IIF(RIGHT(DTOS(ldBirth),4) > RIGHT(DTOS(ldNow),4),1,0)))
      OTHE
         lcPilgrimage = “0”
      ENDCASE
 
      *
      *  add this pilgrim to the display message
      *
      laDups(lnDups,2) = RS_Dup.PM_ID
      laDups(lnDups,1) = TRIM(RS_Dup.PM_cFirst) + TRIM(” “+RS_Dup.PM_cMiddle) ;
            + TRIM(” “+RS_Dup.PM_cLast)+CR+ ;
            IIF(PM_cGender=’F’,’Female age ‘,’Male age ‘)+lcPilgrimage+’, born ‘+DTOC(ldBirth)+CR+ ;
            TRIM(RS_Dup.PM_cCity)+”, “+TRIM(RS_Dup.PM_cState)+”, “+TRIM(RS_Dup.PM_cCountry)
   ENDSCAN
   USE IN RS_Dup
   tnID = 0
 
   *
   *  ask the user whether one of the duplicates is the person she intended
   *   
   DO FORM frmPilgrimdup WITH lnDups,laDups TO tnID
   
   IF tnID > 0    &&  tnID=0 means to continue the Add operation. duplicate is false alarm
      *
      *  here’s the tricky part–convert the Add into an edit for the Pilgrim whose PM_ID is
      *    in tnID. Our ‘Add’ uses thisform.Editaction(), which we might want to cancel.
      *    Also we need to pre-select the pilgrim whose ID is in tnID as if s/he had been
      *    chosen from Page 1
      *
      lcExpression = ‘@tPMID=’+LTRIM(STR(tnID))
      this.Retrievepilgrims(lcExpression)   &&  re-read the hPilgrims settings for (tnID)
      thisform.pilgrimreset()   &&  this will reset child cursors and call newpilgrims(),
                                &&   which will set thisform.nPMID. voila! almost finished.
      *
      *  last part of undoing the Add is reversing the effect of the Editaction() call we made and
      *    the Append Blank we did. The Append Blank was undone by ZAPping curPilgrims in 
      *    the RetrievePilgrims() method. So all that’s left is to undo the Save/Cancel buttons
      *    we’re currently showing.
      *
      ***THISFORM.SetMode(“DEFAULT”)
      this.Refresh()   &&  show the new fields we retrieved
 
      *
      *  Note 09Jan08 tried the call above to SETMODE(). Not only did it not reset the Save/Cancel
      *    buttons, but it also did not show the retrieved fields until I clicked Cancel. So the lesser
      *    evil for now until I figure out the problem is to leave the form in editaction() mode.
      *
   ENDIF
ENDIF
 
RETURN .T.
ENDPROC

…Page2.cntDEpilgrims.FlagChange
Every field that user can edit on the page (in this case Tab 2) has an InteractiveChange method that calls this method to force us into Edit mode, which makes the Save and Cancel buttons appear

*– called by all bound controls interactivechange() methods
PROCEDURE flagchange
LPARAMETERS toControl
 
*
*  Flag this row as Edited
*
thisform.lModpilgrims = .T.
 
RETURN .t. 
ENDPROC

…page2.ctrDEpilgrims.txtbirthdate.Valid
Show age but don’t save it.

PROCEDURE txtbirthdate.Valid
*
*  display the age…BUT WE DON’T SAVE IT in the table any more (for good reason)
*
ldBirth = this.value
ldNow = DATE()
IF ldBirth > ldNow
   ldBirth = DATE(YEAR(ldBirth)-100,MONTH(ldBirth),DAY(ldBirth))
   this.Value = ldBirth
ENDIF
 
DO CASE
CASE ldBirth = DATE(1900,01,01)        &&  this is the birthdate we record when birthdate is missing
   this.Parent.nPilgrimage = 0
CASE !EMPTY(ldBirth) AND TYPE(“ldBirth”)=”D”
   this.parent.nPilgrimage = LTRIM(Str(YEAR(ldNow)-YEAR(ldBirth) ;
      – IIF(RIGHT(DTOS(ldBirth),4) > RIGHT(DTOS(ldNow),4),1,0)))
OTHE
   this.parent.nPilgrimage = 0
   *
   *  04feb16 add the following 2 lines so that a user can not provoke a null birthdate
   *    by blanking out the date as an edit.
   *
   this.Value = DATE(1900,01,01)
   WAIT WINDOW “Birthdate set to 1//1/1900 (means it’s unknown)” timeout(2)
ENDCASE
this.parent.txtAge.Refresh()
 
RETURN DODEFAULT()
ENDPROC

…page2.ctrDEpilgrims.cmdmedical.Click
This prompts for the optional medical notes with (private) medical problems that registration needs to know about e.g. “uses a wheelchair”

PROCEDURE cmdmedical.Click
#DEFINE REQUIRE_PW .F.
*
*  open an edit form for the notes (because the editbox is too small to
*    show a decent amount of the string).
*
LOCAL lcString
lcString = curPilgrims.PM_mMednotes
IF this.parent.openmemoform(@lcString,”Supply Medical/Special Needs Notes”,”frmMemoedit.scx”,REQUIRE_PW)
   *
   *  openmemoform returns .T. if lcString was altered. it also puts the form
   *    into edit mode
   *
   thisform.lMODpilgrims = .T.
   replace curPilgrims.PM_mMednotes WITH lcString IN curPilgrims
   thisform.editaction()
   
   IF EMPTY(lcString)
      *
      *  blend-in colors to indicate no data present
      *
      STORE this.Parent.BackColor TO this.backcolor
      STORE RGB(64,64,64) TO this.forecolor
   ELSE
      *
      *  kind of orange ‘alert’ that data exists
      *
      STORE EVALUATE(this.Parent.nHighlight) TO this.backcolor
      STORE RGB(0,0,0) TO this.forecolor
   ENDIF
 
ENDIF
 
RETURN .T.
ENDPROC

…page2.ctrDEpilgrims.cmdhousekeeping.Click
Similar to Medical Notes. These are Housekeeping Notes–special needs that Housekeeping staff need to be informed about. Huware opens a popup form that allows a lot of data to be entered

PROCEDURE cmdhousekeeping.Click
#DEFINE REQUIRE_PW .F.
*
*  open an edit form for the notes (because the editbox is too small to
*    show a decent amount of the string).
*
LOCAL lcString
lcString = curPilgrims.PM_mHKnotes
 
IF this.parent.openmemoform(@lcString,”Supply Special Housekeeping Requirements”,”frmMemoedit.scx”,REQUIRE_PW)
   *
   *  openmemoform returns .T. if lcString was altered. it also puts the form
   *    into edit mode
   *
   thisform.lModpilgrims = .T.
   replace curPilgrims.PM_mHKnotes WITH lcString IN curPilgrims
   thisform.editaction()
 
   IF EMPTY(lcString)
      *
      *  blend-in colors to indicate no data present
      *
      STORE this.Parent.BackColor TO this.backcolor
      STORE RGB(64,64,64) TO this.forecolor
   ELSE
      *
      *  kind of orange ‘alert’ that data exists
      *
      STORE EVALUATE(this.Parent.nHighlight) TO this.backcolor
      STORE RGB(0,0,0) TO this.forecolor
   ENDIF
 
ENDIF
 
RETURN .T.
ENDPROC

…page2.ctrDEpilgrims.cmdregnotes.Click
Registration notes, also entered into a popup window.

PROCEDURE cmdregnotes.Click
#DEFINE REQUIRE_PW .T.
*
*  open an edit form for the notes (because the editbox is too small to
*    show a decent amount of the string).
*
LOCAL lcString
lcString = curPilgrims.PM_mRegnotes
 
IF this.parent.openmemoform(@lcString,”Supply Registration Notes”,”frmMemoedit.scx”,REQUIRE_PW)
   *
   *  openmemoform returns .T. if lcString was altered. it also puts the form
   *    into edit mode
   *
   thisform.lMODpilgrims = .T.
   replace curPilgrims.PM_mRegnotes WITH lcString IN curPilgrims
   thisform.editaction()
   
   IF EMPTY(lcString)
      *
      *  blend-in colors to indicate no data present
      *
      STORE this.Parent.BackColor TO this.backcolor
      STORE RGB(64,64,64) TO this.forecolor
   ELSE
      *
      *  kind of orange ‘alert’ that data exists
      *
      STORE EVALUATE(this.Parent.nHighlight) TO this.backcolor
      STORE RGB(0,0,0) TO this.forecolor
   ENDIF
 
ENDIF
 
RETURN .T.
ENDPROC

…page2.ctrDEpilgrims.chkcform.Valid
The C-form is required by the government for all foreign pilgrims visiting Meherabad. Country of residence is not a valid indication of Indian or Foreign status. A European couple might have an India residence address. A Indian citizen might have an American address.

PROCEDURE chkcform.Valid
 
*
*  new special processing for the lCform flag. Added 15dec14. this duplicates logic from
*    the txyCountry method.
*
IF (curPilgrims.PM_lCform=0 AND UPPER(curPilgrims.PM_cCountry) != “INDIA”) OR ;
   (curPilgrims.PM_lCform=1 AND UPPER(curPilgrims.PM_cCountry) = “INDIA”)
   *
   *  colorize an unusual case as an exception. even though this pilgrim has an Indian addr
   *    s/he needs a C Form for the police. e.g. person is a PIO, not an Indian citizen, but residing in India.
   *
   STORE EVALUATE(this.parent.nHighlight) TO this.BackColor
   ***STORE RGB(0,0,0) TO this.foreColor
ELSE
   *
   *  normal colors for a normal case. either an Indian who does not need a C form or a foreigner
   *    who does need a C form.
   *
   STORE this.parent.BackColor TO this.BackColor
   ***STORE RGB(64,64,64) TO this.foreColor
ENDIF
 
this.Refresh()
 
RETURN DODEFAULT()
ENDPROC

FORM frmPilgrims - Tab 1 (Pilgrims) VFP Method Code

Introduction
This code manages saving pilgrim information to the hPilgrims table via the savepilgrims method. Also keeps track of carry-over data for pilgrims, keeping the curPilgrims result set temp table fresh, notes whether the current field values are obsolete (if a different pilgrim has been selected on Tab1 or the Ad Pilgrim button has been clicked on Tab1.

frmPilgrims.grdNav.Click
This is a simple override so that a single click of a row on the pilgrim selection grid on Tab 1 results in selecting that pilgrim

PROCEDURE Pgfpagerefresh1.Page1.grdNav.Click
LOCAL lok
 
*
*  make single click into a “selection” action. 15 June 06 experiment,
*    to work around loss of synchronization caused when the user
*    single clicks a row on the grid and jumps past page 2, by
*    directly clicking page 3, 4, or 5. This bypass of Selectionmade()
*    prevents us from calling page2.(container).newpilgrims() which
*    is essential to all navigation integrity…
*
lok = DODEFAULT()
   
THIS.SelectionMade()
RETURN lok
ENDPROC
 

frmPilgrims.grdNav.SelectionMade
This is fired by the method above to check whether a real selection was made. If the Pilgrim’s primary key has not changed it isn’t really a selection.

PROCEDURE Pgfpagerefresh1.Page1.grdNav.selectionmade
LOCAL lok
 
*
*  As of Nov 2005 (EARLY devel stage) I’m not sure we need to call the other
*    initialization methods. It may turn out that we manage them entirely in commonpageactivate,
*    wherein we’d prevent any page from exposing the properties of a previous Pilgrims record
*    until the Visits and Booking Selections have been made…(TRUE!)
*
 
**WAIT WINDOW “Selection made” timeout(2)
IF curPilgrims.PM_ID <> thisform.nPMID
   *
   *  user selected a new or different Pilgrim
   *
   STORE 0 TO thisform.nVTID, thisform.nBKID
ENDIF
thisform.nPMID = curPilgrims.PM_ID
*
*  added 15jan10 out of superstition, pilgrim has changed and we don’t yet know 
*    her/his Visit’s VT_GPID, if any. So we’re just cleaning up any old lingering value.
*
thisform.nGPID = 0   
 
*
*  clear out all cursors at the start so we NEVER allow the user to jump to a page and see
*    old grid data from a previous pilgrim. also calls page2 newpilgrims()
*
thisform.pilgrimreset()
 
RETURN DODEFAULT()
ENDPROC

frmPilgrims.grdNav.allownavigation
Prevent shifting to (selecting) a different pilgrim if the currently-selected one has a Save or Cancel pending

PROCEDURE Pgfpagerefresh1.Page1.grdNav.allownavigation
*
*  XXFWFRM.VCX/grdNavigate::AllowNavigation()
*
*  the idea is to deny record navigation if THISFORM 
*  is in Add/Edit mode
*
*  called from:
*    THIS.When()
*    THIS.UIEnable()
*    XXFWFRM.VCX/txtGridNavFind.When()
*    XXFWFRM.VCX/tbrSCADONav.cmdFind.Click()
*
LPARAMETERS tlMDown  
 
LOCAL lcMode, llRetVal
lcMode = THISFORM.GetPProp(“icMode”)
 
llRetVal = .t.
IF lcMode <> “DEFAULT”  &&  AND tlMDown  (don’t know why VMP includes this test–experiment to remove it)
   *
   *  THISFORM.GetMode() = “ADD” or “EDIT”
   *
   DO CASE
   CASE thisform.lModPilgrims
      =MESSAGEBOX(“First save changes for this Pilgrim”,MBEO,”Save/Cancel Pending (1)”)
 
   CASE thisform.lModVisits
      =MESSAGEBOX(“First save changes for this Pilgrim Visit”,MBEO,”Save/Cancel Pending (1)”)
 
   CASE thisform.lModBookings
      =MESSAGEBOX(“First save changes for this Pilgrim Booking”,MBEO,”Save/Cancel Pending (1)”)
 
   CASE thisform.lModRooms
      =MESSAGEBOX(“First save changes for this Pilgrim’s room assignment(s)”,MBEO,”Save/Cancel Pending (1)”)
   ENDCASE 
 
   llRetVal = .f.
ENDIF
 
RETURN llRetVal
ENDPROC

frmPilgrims.[method name]
Comments
Actual code

frmPilgrims.NewPilgrims
Prep for a new pilgrim selection, See the comments.

*– prime the properties with fresh pilgrim fields from SQL Server
PROCEDURE newpilgrims
LPARAMETERS tlReset
* —————————————————————————
*
*  thisform.NewPilgrims() method. Called from thisform.pgfpageframe1.grdnav.selectionmade()
*    also called from thisform.pgfpageframe1.ctrSelPilgrims1.cmdApply.Click if only
*    one record comes from the server based on the selection criteria.
*  20mar09 we now also call this from the cancelpilgrims() method as part of the
*    procedure to restore the pilgrim information after a Cancel button click.
*
* —————————————————————————
LOCAL LOK
 
IF PARAMETERS() < 1
   tlReset = .F.
ENDIF
 
*
*  since Pilgrim is new, the form’s retained IDs of child and grandchild
*    IDs are all now obsolete
*
thisform.nVTID = 0
thisform.nBKID = 0
 
*
*  (added these resets 16jul07). when pilgrim changes, all these values become
*    obsolete. note that when visit changes, booking becomes obsolete, so we
*    need to put a reduced version of this code into newvisits. also a further
*    reduced on into newbookings.
*
STORE 0 TO thisform.nPage3PMID, ;
   thisform.nPage4PMID, thisform.nPage4VTID, ;
   thisform.nPage5PMID, thisform.nPage5VTID, thisform.nPage5BKID
 
*
*  test for option to clear out properties. we may never need this FOR PILGRIMS,
*    but we certainly will need it for Visits and Bookings when the user clicks the
*    Clear button on page 1.
*
IF tlReset
   *
   *  Indicate no Pilgrim is selected yet
   *
   thisform.nPMID = 0
 
   *
   *  Empty the cursor to clear the navigation grid
   *
   SELECT curPilgrims
   this.clearcursor()
   
   *
   *  make blank-looking buttons since there’s no info behind them at this point.
   *
   STORE this.BackColor TO this.cmdHousekeeping.BackColor, this.cmdMedical.backcolor
   STORE RGB(64,64,64) TO this.cmdHousekeeping.foreColor, this.cmdMedical.forecolor
   this.nPilgrimage = 0
 
   this.Refresh()
   RETURN .T.
ENDIF
 
*
*  manage the calculated Age field and the colors of the command buttons
*
this.refreshpilgrims()
 
*
*  experimental code added 15dec07. If we’re doing an Add Pilgrim operation, this
*    method would be finished, because it would be called from the Add Pilgrim
*    command button with the tlRest parameter set to .T.
*  by falling through here we know we have come from Selectionmade() of the grid
*    and we have an existing pilgrim to set up for.
*  The experiment is to put this pilgrim’s data into the carry-forward parameter
*    block. So before you add a new pilgrim, if you simply retrieve (not add)
*    a relative, you get all the address data in the block and your right-click
*    of Add Pilgrim then will let you bring forward a lot of data.
*
LOCAL loBlock
IF TYPE(“thisform.oPMparamblock”) = “O”
   loBlock = thisform.oPMparamblock
   lnRows = ALEN(loBlock.aParameters,1)
   FOR ln = 1 TO lnRows
      lcField = loBlock.aParameters(ln,PB_FIELD_NAME)
      luValue = &lcField
      loBlock.aParameters(ln,PB_FIELD_VALUE) = luValue   &&  may be empty–that’s ok
   ENDFOR
ENDIF
 
RETURN .T.
ENDPROC

frmPilgrims.crtDEpilgrims.savepilgrims
Save just the info from Tab2 as a new or updated pilgrim.

*– called from thisform.saveaction() method to do the work of saving a new or edited Pilgrims rcd
PROCEDURE savepilgrims
LPARAMETERS toBlock
*
* the form’s saveaction method first calls this method to save Pilgrim info,
*    then any Visits, and finally any Bookings. That order is determined by
*    the possibilty that we added a Pilgrims row, in which case we need the ID
*    to put into hVisits, and/or we added a Visits row in which case we need
*    the new Visits ID for the Bookings.
*
*  4June06 added the parameter in support of the new carry forward feature
 
PRIVATE nIDvalue
LOCAL lcName,lcDatetime,lcTemp
 
 
*
*  get the handle first because we know a row needs saving (because we were called by form’s
*    saveaction method based on testing thisform.lModpilgrims)
*
lnHandle = oLib.oConnectionSVC.GetSingleConnectHandle()
IF lnHandle <= 0
   =MESSAGEBOX(“Unable to establish contact with the SQL Server computer”,MBEO,”Connectivity Problem”)
   RETURN .F.
ENDIF
 
*
*  24jan15 new code added because SQL Server will bomb if we try to add a row with no gender.
*    He’re we’ll handle that situation more gracefully by detecting it in advance. IF WE EVER
*    need to allow a missing Gender (e.g. for members of a family staying in some new kind
*    of family quarters, we’ll need to modify (at least) the hPilgrims insert trigger.
*
IF ATC(curPilgrims.PM_cGender,”MF”) < 1
   =MESSAGEBOX(“Pilgrim’s gender must be set to ‘M’ or ‘F’ or we can’t do the save.”,MBEO,”Gender required”)
   RETURN .F.
ENDIF
 
*
*  defend ourselves against a SQL Server crash caused by single quote, unpaired, in any character
*   property
*
REPLACE ;
   PM_cFirst    WITH thisform.fixquote(curPilgrims.PM_cFirst), ;
   PM_cMiddle   WITH thisform.fixquote(curPilgrims.PM_cMiddle), ;
   PM_cLast     WITH thisform.fixquote(curPilgrims.PM_cLast), ;
   PM_cNicknm   WITH thisform.fixquote(curPilgrims.PM_cNicknm), ;
   PM_cAddr1    WITH thisform.fixquote(curPilgrims.PM_cAddr1), ;
   PM_cAddr2    WITH thisform.fixquote(curPilgrims.PM_cAddr2), ;
   PM_cAddr3    WITH thisform.fixquote(curPilgrims.PM_cAddr3), ;
   PM_cCity     WITH thisform.fixquote(curPilgrims.PM_cCity), ;
   PM_cState    WITH thisform.fixquote(curPilgrims.PM_cState), ;
   PM_cZcode    WITH thisform.fixquote(curPilgrims.PM_cZcode), ;
   PM_cCountry  WITH thisform.fixquote(curPilgrims.PM_cCountry), ;
   PM_cPhone    WITH thisform.fixquote(curPilgrims.PM_cPhone), ;
   PM_cPhone2   WITH thisform.fixquote(curPilgrims.PM_cPhone2), ;
   PM_cCell     WITH thisform.fixquote(curPilgrims.PM_cCell), ;
   PM_cEmail    WITH thisform.fixquote(curPilgrims.PM_cEmail), ;
   PM_mNotes    WITH thisform.fixquote(curPilgrims.PM_mNotes), ;
   PM_mMednotes WITH thisform.fixquote(curPilgrims.PM_mMednotes), ;
   PM_mHKnotes  WITH thisform.fixquote(curPilgrims.PM_mHKnotes), ;
   PM_mRegnotes WITH thisform.fixquote(curPilgrims.PM_mRegnotes), ;
   PM_mRelated  WITH thisform.fixquote(curPilgrims.PM_mRelated), ;
   PM_mOccup    WITH thisform.fixquote(curPilgrims.PM_mOccup) IN curPilgrims
 
*
*  13dec18 new mods to include userid and date in the parameters. there are
*    corresponding mods in the stored procedures and triggers for hPilgrims.
*
lcName = IIF(TYPE(“oApp.cUsername”) = “C”, ;
   oApp.cUsername, “Unknown/Unknown”)
 
*
*  first convert today’s date to char, like ’25/12/2025′, then convert
*    to the form ‘2025-12-25’
*
*  25dec18 modified to include time as well as date in SQL server calls
*
SET CENTURY ON
lcTemp = DTOC(DATETIME())
lcDatetime = SUBSTR(lcTemp,7,4)+’-‘+SUBSTR(lcTemp,4,2)+’-‘+LEFT(lcTemp,2)+’ ‘+TIME()
 
*
*  are we doing an add or an edit? pcMode won’t help. we look at the id number. If an add
*    there’s no primary key assigned yet by SQL Server. We assigned a temporary key value of -1.
*
DO CASE
CASE thisform.nPMID < 0
   *
   *  This is an Add. We know because the PM_ID has not yet been created.
   *
   *  manage missing birthdate. If the date is truly not given below, the SP will
   *    reject the INSERT. So here we dummy up a missing date by using the constant 
   *    01/01/1900. (Added this code 09Oct06 as partial response to Irene’s complaint
   *    that New Pilgrims with missing gender and date of birth are rejected. Still
   *    don’t want to allow bad or incomplete data to be entered. How can we accept someone
   *    of no known age and gender? Where to book him/her/it?)
   *
   IF EMPTY(curPilgrims.PM_dBirth)
      replace PM_dBirth WITH DATE(1900,01,01) IN curPilgrims
   ENDIF
 
   *
   *  new 15jan15. here we protect against the VALID of the prompt for PM_lCform not having
   *    fired. Initially on a new pilgrim Add, this is set to -1. If on coming here it’s still -1, then
   *    we’ll derive the likely value here because we can’t tolerate anything but a 0 or a 1 in the field.
   *
   IF curPilgrims.PM_lCform = -1
      replace PM_lCform WITH IIF(UPPER(ALLTRIM(curPilgrims.PM_cCountry)) = ‘INDIA’,0,1) IN curPilgrims
   ENDIF
 
   * —————————————————————–
   *
   *  new code 11July07. Here we check for an existing record with same
   *    name as the one we are adding, and warn the user if it exists.
   *
   * —————————————————————–
   *
   *  prep parameter list for the Retrieve SP
   *
   nIDvalue = 0
   lcExpression = “”
   IF !EMPTY(curPilgrims.PM_cFirst)
      IF !EMPTY(lcExpression)
         lcExpression = lcExpression + “, “
      ENDIF
      lcExpression = lcExpression + “@tFirst='”+strtran(TRIM(curPilgrims.PM_cFirst),”‘”,””””)+”‘”
   ENDIF 
   IF !EMPTY(curPilgrims.PM_cLast)
      IF !EMPTY(lcExpression)
         lcExpression = lcExpression + “, “
      ENDIF
      lcExpression = lcExpression + “@tLast='”+strtran(TRIM(curPilgrims.PM_cLast),”‘”,””””)+”‘”
   ENDIF 
   IF !EMPTY(curPilgrims.PM_cMiddle)
      IF !EMPTY(lcExpression)
         lcExpression = lcExpression + “, “
      ENDIF
      lcExpression = lcExpression + “@tMiddle='”+strtran(TRIM(curPilgrims.PM_cMiddle),”‘”,””””)+”‘”
   ENDIF 
   lcExpression = lcExpression + “, @tScope=3 “
 
   *
   *  do the lookup
   *
   lcCmd = “EXEC dbo.USP_Ret_Pilgrims “+lcExpression
   lnResult = H2SQLexe(lnHandle, lcCmd, “RS_Dup”)
   IF lnResult > 0 AND USED(“RS_Dup”) AND RECCOUNT(“RS_Dup”) >= 1
      *
      *  have a dup record, maybe even 2+!
      *
      lcMessage = “Found one or more Pilgrim record(s) with matching name:”+CR2
         SELECT RS_Dup
         SCAN
            lcMessage = lcMessage + TRIM(RS_Dup.PM_cFirst) + TRIM(” “+RS_Dup.PM_cMiddle) + TRIM(” “+RS_Dup.PM_cLast)+CR+ ;
               TRIM(RS_Dup.PM_cCity)+”, “+TRIM(RS_Dup.PM_cState)+”, “+TRIM(RS_Dup.PM_cCountry)+CR
            IF !EMPTY(RS_Dup.PM_dBirth) AND RS_Dup.PM_dBirth != DATE(1900,01,01)
               lcTemp = DTOS(RS_Dup.PM_dBirth)
               lcMessage = lcMessage + “Born “+LEFT(lcTemp,4)+”/”+SUBSTR(lcTemp,5,2)+”/”+SUBSTR(lcTemp,7)+CR2
            ELSE
               lcMessage = lcMessage + “(Unknown birth date)”+CR2
            ENDIF 
         ENDSCAN
      IF IDNO = MESSAGEBOX(lcMessage+”Yes–Go ahead and ADD this pilgrim anyway (not a duplicate)”+CR+”No–Don’t add, it really is a duplicate”,MBQYN,”Apparent Duplicate Pilgrim!”)
         USE IN RS_Dup
         RETURN .F.
      ENDIF
   ENDIF
   IF USED(“RS_Dup”)
      USE IN RS_Dup
   ENDIF
   
 
   *
   *  prep parameter list for the Insert SP
   *
   *  06jan19 modified this a little to put an ‘*’ prefix on PM_cIDupdate, which reassures 
   *    the UPDATE trigger that we have already supplied ID and date. No need for it
   *    to overwrite these from the trigger. Benefit is that in Windows we can determine the 
   *    actual Win login ID, whereas Azure can only see the ID used in the connection string.
   *
   lcExpression = “?@nIDvalue, “+ ;
“@PM_cFirst='”+TRIM(curPilgrims.PM_cFirst)+”‘, “+ ;
“@PM_cMiddle='”+TRIM(curPilgrims.PM_cMiddle)+”‘, “+ ;
“@PM_cLast='”+TRIM(curPilgrims.PM_cLast)+”‘, “+ ;
“@PM_cNicknm='”+TRIM(curPilgrims.PM_cNicknm)+”‘, “+ ;
“@PM_cAddr1='”+TRIM(curPilgrims.PM_cAddr1)+”‘, “+ ;
“@PM_cAddr2='”+TRIM(curPilgrims.PM_cAddr2)+”‘, “+ ;
“@PM_cAddr3='”+TRIM(curPilgrims.PM_cAddr3)+”‘, “+ ;
“@PM_cCity='”+TRIM(curPilgrims.PM_cCity)+”‘, “+ ;
“@PM_cState='”+TRIM(curPilgrims.PM_cState)+”‘, “+ ;
“@PM_cZCode='”+TRIM(curPilgrims.PM_cZCode)+”‘, “+ ;
“@PM_cCountry='”+TRIM(curPilgrims.PM_cCountry)+”‘, “+ ;
“@PM_cGender='”+TRIM(curPilgrims.PM_cGender)+”‘, “+ ;
“@PM_dBirth=”+IIF(!EMPTY(curPilgrims.PM_dBirth),”‘”+STUFF(STUFF(DTOS(curPilgrims.PM_dBirth),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
“@PM_cPhone='”+TRIM(curPilgrims.PM_cPhone)+”‘, “+ ;
“@PM_cPhone2='”+TRIM(curPilgrims.PM_cPhone2)+”‘, “+ ;
“@PM_cCell='”+TRIM(curPilgrims.PM_cCell)+”‘, “+ ;
“@PM_cEmail='”+TRIM(curPilgrims.PM_cEmail)+”‘, “+ ;
“@PM_mNotes='”+TRIM(LEFT(curPilgrims.PM_mNotes,250))+”‘, “+ ;
“@PM_mMednotes='”+TRIM(LEFT(curPilgrims.PM_mMednotes,250))+”‘, “+ ;
“@PM_mHKnotes='”+TRIM(LEFT(curPilgrims.PM_mHKnotes,250))+”‘, “+ ;
“@PM_mRegnotes='”+TRIM(LEFT(curPilgrims.PM_mRegnotes,250))+”‘, “+ ;
“@PM_mRelated='”+TRIM(LEFT(curPilgrims.PM_mRelated,250))+”‘, “+ ;
“@PM_mOccup='”+TRIM(LEFT(curPilgrims.PM_mOccup,250))+”‘, “+ ;
“@PM_lCform='”+STR(curPilgrims.PM_lCform,1)+”‘, “+ ;
    “@PM_cIDCreate='”+lcName+”‘, “+ ;
    “@PM_dCreate='”+lcDatetime+”‘, “+ ;
    “@PM_cIDupdate=’*”+lcName+”‘, “+ ;
    “@PM_dUpdate='”+lcDatetime+”‘”
 
 
   *
   *  insert the row
   *
   lcCmd = “EXEC dbo.USP_Pilgrims_Insert “+lcExpression
   lnResult = H2SQLexe(lnHandle, lcCmd)
 
   IF nIDvalue > 0
      *
      *  insert worked, so retain the real SQL Server-assigned primary key. 
      *
      thisform.nPMID = nIDvalue
      
      *
      *  essential that we update our cursor with the SQLassigned new ID.
      *    Plan B would be to call findpilgrims() and let it re-retrieve the
      *    result set and rebuild curPilgrims, as we do (only) with ctrDEvisits,
      *    (not ctrDEBookings or ctrDERoom)
      *
      replace PM_ID WITH nIDvalue IN curPilgrims
   ELSE
      *  error handling needed here!
      =MESSAGEBOX(“Unable to insert a new Pilgrims record in the database”,MBEO,”Data Problem”)
      RETURN .F.
   ENDIF
   
   *
   *  now retain the latest field values in the carry forward parameter block
   *
   this.savecarry(toBlock)
 
CASE thisform.nPMID > 0
   *
   *  prep the Update param list. Note that the SP logic (at least as first conceived)
   *    requires that we send all fields back, whether they were modified or not.
   *    The Update trigger will determine which fields (if any) were changed,
   *    spin off a hChanges row, and update the timestamp and ID of the Pilgrims row.
   *
   *  06jan19 added the * prefix on PM_cIDupdate, see above.
   *
      PRIVATE nResult
      nResult = 0
      lcExpression = “?@nResult, “+ ;
“@PM_ID=”+LTRIM(STR(thisform.nPMID))+”, “+ ;
“@PM_cFirst='”+TRIM(curPilgrims.PM_cFirst)+”‘, “+ ;
“@PM_cMiddle='”+TRIM(curPilgrims.PM_cMiddle)+”‘, “+ ;
“@PM_cLast='”+TRIM(curPilgrims.PM_cLast)+”‘, “+ ;
“@PM_cNicknm='”+TRIM(curPilgrims.PM_cNicknm)+”‘, “+ ;
“@PM_cAddr1='”+TRIM(curPilgrims.PM_cAddr1)+”‘, “+ ;
“@PM_cAddr2='”+TRIM(curPilgrims.PM_cAddr2)+”‘, “+ ;
“@PM_cAddr3='”+TRIM(curPilgrims.PM_cAddr3)+”‘, “+ ;
“@PM_cCity='”+TRIM(curPilgrims.PM_cCity)+”‘, “+ ;
“@PM_cState='”+TRIM(curPilgrims.PM_cState)+”‘, “+ ;
“@PM_cZCode='”+TRIM(curPilgrims.PM_cZCode)+”‘, “+ ;
“@PM_cCountry='”+TRIM(curPilgrims.PM_cCountry)+”‘, “+ ;
“@PM_cGender='”+TRIM(curPilgrims.PM_cGender)+”‘, “+ ;
“@PM_dBirth=”+IIF(!EMPTY(curPilgrims.PM_dBirth),”‘”+STUFF(STUFF(DTOS(curPilgrims.PM_dBirth),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
“@PM_cPhone='”+TRIM(curPilgrims.PM_cPhone)+”‘, “+ ;
“@PM_cPhone2='”+TRIM(curPilgrims.PM_cPhone2)+”‘, “+ ;
“@PM_cCell='”+TRIM(curPilgrims.PM_cCell)+”‘, “+ ;
“@PM_cEmail='”+TRIM(curPilgrims.PM_cEmail)+”‘, “+ ;
“@PM_mNotes='”+TRIM(LEFT(curPilgrims.PM_mNotes,250))+”‘, “+ ;
“@PM_mMednotes='”+TRIM(LEFT(curPilgrims.PM_mMednotes,250))+”‘, “+ ;
“@PM_mHKnotes='”+TRIM(LEFT(curPilgrims.PM_mHKnotes,250))+”‘, “+ ;
“@PM_mRegnotes='”+TRIM(LEFT(curPilgrims.PM_mRegnotes,250))+”‘, “+ ;
“@PM_mRelated='”+TRIM(LEFT(curPilgrims.PM_mRelated,250))+”‘, “+ ;
“@PM_mOccup='”+TRIM(LEFT(curPilgrims.PM_mOccup,250))+”‘, “+ ;
“@PM_lCform='”+STR(curPilgrims.PM_lCform,1)+”‘, “+ ;
    “@PM_cIDUpdate=’*”+lcName+”‘, “+ ;
    “@PM_dUpdate='”+lcDatetime+”‘”
 
   *
   *  update the row
   *
   lcCmd = “EXEC dbo.USP_Pilgrims_Update “+lcExpression
   lnResult = H2SQLexe(lnHandle, lcCmd)
   IF lnResult <= 0 OR nResult < 0
      *  error handling needed here!
   ENDIF
   
   *
   *  now retain the latest field values in the carry forward parameter block
   *
   this.savecarry(toBlock)
ENDCASE
 
RETURN .T.
ENDPROC

frmPilgrims.CancelPilgrims
Cleanup after op clicked Cancel

*– handle failed add or edit in the most graceful way
PROCEDURE cancelpilgrims
 
*
*  handle an aborted Add. two reasons are possible: no name/gender, or duplicate record exists
*
IF thisform.nPMID < 0
   thisform.nPMID=0
   thisform.lModpilgrims = .F.
   this.clearcursor()
   thisform.pgfPagerefresh1.Activepage = 1
   WAIT WINDOW “Pilgrim not added” timeout(0.7)
ELSE
   *
   *  20mar09 musings about a canceled edit. When the user alters a curPilgrims row
   *    and then clicks Cancel, there was no code here to restore the curPilgrims 
   *    fields to their former value. The pilgrim looks modified (though it isn’t).
   *  in the corresponding cancel(visits, bookings, room) methods we’d call the
   *    find(visits,bookings,room) methods to rebuild the cursor, so the Cancel
   *    effect would be visible.
   *  for pilgrims, however, there is no findpilgrims() method in this container,
   *    so restoring the pilgrim row will not follow the same path. This issue came up
   *    when demoing Medical Needs notes data entry to Pat. You add medical notes,
   *    then click OK. The Save/Cancel butons appear. You choose Cancel, but you find
   *    that the notes still seem to be there.
   *
   lnid = curPilgrims.PM_ID        &&  remember what row we’re working on
   *
   *  re-run the retrieve that was used originally by the Apply button on page 1.
   *    It was saved by the original call to retrievepilgrims()
   *
   this.retrievepilgrims(this.cRetrieveExpression)
   SELECT curPilgrims
   LOCATE FOR PM_ID = lnid
   this.newpilgrims()
   
ENDIF
 
RETURN .T.
ENDPROC

frmPilgrims.ctrDEpilgrims.SaveCarry
There is a method with identical logic in each Tab container,. This one saves information from the Pilgrims row.

*– during SavePilgrims, we call this method to retain altered values in the parameter block passed by the form
PROCEDURE savecarry
LPARAMETERS toBlock
LOCAL ln, lnrows, lcField
 
*
*  if the user has invoked the ‘carry’ options, we save the demographic fields in
*    a parameter block just after they have been written.
*
lnRows = ALEN(toBlock.aParameters,1)
FOR ln = 1 TO lnRows
   lcField = toBlock.aParameters(ln,PB_FIELD_NAME)
   luValue = &lcField
   toBlock.aParameters(ln,PB_FIELD_VALUE) = luValue   &&  may be empty–that’s ok
ENDFOR
 
RETURN .t.
ENDPROC
 
 
*– clear the carry forward parameter block retained data when a LEFT click of Add Pilgrim is received.
PROCEDURE clearcarry
lparameters toBlock
 
*
*  need to clear the carry forward data for pilgrims, since the new pilgrim is NOT using
*    carry forward and we don’t want the last guy’s data to clobber what we are about
*    to enter when the user comes to this.savecarry()
*
lnRows = ALEN(toBlock.aParameters,1)
FOR ln = 1 TO lnRows
   lcField = toBlock.aParameters(ln,PB_FIELD_NAME)
   lcType = TYPE(lcField)
   *
   *  note–pretty sure all field values are Character…
   *
   DO CASE
   CASE lcType = “C”
      STORE “” TO toBlock.aParameters(ln,PB_FIELD_VALUE)
   CASE lcType = “N”
      STORE 0 TO toBlock.aParameters(ln,PB_FIELD_VALUE)
   CASE lcType = “L”
      STORE .F. TO toBlock.aParameters(ln,PB_FIELD_VALUE)
   ENDCASE
ENDFOR
 
RETURN .T.
ENDPROC

frmPilgrims.ctrDEpilgrims.refreshpilgrims
See Comments below–

*– needed when user has altered properties, then clicked Cancel. We restore the current settings.
PROCEDURE refreshpilgrims
 
*
*  new regime–no more properties. We show the curPilgrims selected row in the
*    page 2 fields. So all we need to do here is manage the buttons on page 2
*    that change color if the memo field contents are present.
*
 
IF EMPTY(curPilgrims.PM_mMednotes)
   *
   *  blend-in colors to indicate no data present
   *
   STORE this.BackColor TO this.cmdMedical.backcolor
   STORE RGB(64,64,64) TO this.cmdMedical.forecolor
ELSE
   *
   *  kind of orange ‘alert’ that data exists
   *
   STORE EVALUATE(this.nHighlight) TO this.cmdMedical.backcolor
   STORE RGB(0,0,0) TO this.cmdMedical.forecolor
ENDIF
 
*  same deal for this button…
IF EMPTY(curPilgrims.PM_mHKnotes)
   STORE this.BackColor TO this.cmdHousekeeping.BackColor
   STORE RGB(64,64,64) TO this.cmdHousekeeping.foreColor
ELSE
   STORE EVALUATE(this.nHighlight) TO this.cmdHousekeeping.BackColor
   STORE RGB(0,0,0) TO this.cmdHousekeeping.foreColor
ENDIF
 
*  same deal for this button…
IF EMPTY(curPilgrims.PM_mRegnotes)
   STORE this.BackColor TO this.cmdRegnotes.BackColor
   STORE RGB(64,64,64) TO this.cmdRegnotes.foreColor
ELSE
   STORE EVALUATE(this.nHighlight) TO this.cmdRegnotes.BackColor
   STORE RGB(0,0,0) TO this.cmdRegnotes.foreColor
ENDIF
 
*
*  new special setup processing for the lCform flag. Added 15dec14
*
IF (curPilgrims.PM_lCform=0 AND UPPER(curPilgrims.PM_cCountry) != “INDIA”) OR ;
   (curPilgrims.PM_lCform=1 AND UPPER(curPilgrims.PM_cCountry) = “INDIA”)
   *
   *  colorize an unusual case as an exception. even though this pilgrim has an Indian addr
   *    s/he needs a C Form for the police. e.g. person is a PIO, not an Indian citizen, but residing in India.
   *
   STORE EVALUATE(this.nHighlight) TO this.chkCform.BackColor
   ***STORE RGB(0,0,0) TO this.chkCform.foreColor
ELSE
   *
   *  normal colors for a normal case. either an Indian who does not need a C form or a foreigner
   *    who does need a C form.
   *
   STORE this.BackColor TO this.chkCform.BackColor
   ***STORE RGB(64,64,64) TO this.chkCform.foreColor
ENDIF
 
 
*
*  we display the age BUT WE DON’T SAVE IT in the table any more (for good reason)
*
ldBirth = curPilgrims.PM_dBirth
ldNow = DATE()
IF ldBirth > ldNow
   ldBirth = DATE(YEAR(ldBirth)-100,MONTH(ldBirth),DAY(ldBirth))
   replace PM_dBirth WITH ldBirth IN curPilgrims
ENDIF
DO CASE
CASE ldBirth = DATE(1900,01,01)        &&  this is the birthdate we record when birthdate is missing
   this.nPilgrimage = 0
CASE !EMPTY(ldBirth) AND TYPE(“ldBirth”)=”D”
   this.nPilgrimage = LTRIM(Str(YEAR(ldNow)-YEAR(ldBirth) ;
      – IIF(RIGHT(DTOS(ldBirth),4) > RIGHT(DTOS(ldNow),4),1,0)))
OTHE
   this.nPilgrimage = 0
ENDCASE
this.txtAge.Refresh()
 
RETURN .T.
ENDPROC

frmPilgrims.Pgfpagerefresh1.commonpageactivate
This runs any time any page (i.e. Tab) is selected

PROCEDURE Pgfpagerefresh1.commonpageactivate
LPARAMETERS toPage
 
*
*  now normal page processing for the newly requested page
*
LOCAL lnPage, lok
lok = DODEFAULT(toPage)
 
*
*  Major action of this form is handled here when pages change. Our information comes like this:
*    on pilgrim navigation (page 1, container, grdnav) we save the new PMID to the form
*    on visits navigation (page 3, container, grdnav) we save the new VTID to the form
*    on bookings navigation (page 4, grddebook, afterrowcolchange) we save the new BKID to the form
*
*    in ctrDEpilgrims.newvisits() we record the PMID in the thisform.nPage3PMID property
*    in ctrDEbookings.newbookings() we record the PMID and VTID as thisform.nPage4PMID, VTID
*    in ctrDEroom.newroom() we record the PMID, VTID and BKID as as thisform.nPage5PMID, VTID, BKID
*
*    in pageactivate, we observe whether at each page level the current ID (in the form) matches
*      the ID last handled by the container and respond if they are out of step.
*
lnPage = thisform.pageactivate(RIGHT(toPage.Name,1))    &&  this will work UNLESS someone renames 
                                                        &&    the pages…so don’t do it.
*
*  test for page movement error. so far, it always means that a save/cancel is pending.
*
IF lnPage < 0
   thisform.pgfpagerefresh1.ActivePage = 1     &&  I think this will re-fire commonpageactivate…
   RETURN .F.
ENDIF
 
*
*  deferred change of alt page, requested in pageactivate method, delayed until dodefault() is done
*
IF lnPage > 0 AND lok
   thisform.pgfpagerefresh1.ActivePage = lnPage
ENDIF
 
*
*  NOW deferred refresh(). See ctrDEroom.newroom() for extensive comments about an odd
*    problem in which a refresh() not preceeded by a [wait window] or even just a 
*    [wait “” timeout(0.05)] fails to refresh the curRooms grid on page 5. So the premise
*    is that by delaying the refresh to here we can get it to work without the arbitrary delay.
*
****WAIT WINDOW “Refreshing page “+STR(thisform.pgfpagerefresh1.ActivePage,1) timeout(0.4)
thisform.pgfpagerefresh1.objects(thisform.pgfpagerefresh1.ActivePage).Refresh()
 
RETURN lok
ENDPROC

frmPilgrims.Page1.Activate
This method runs whenever the Tab 1 is clicked on (activated). IF the form was called with the PM_ID supplied as a parameter, we want to go to the Visits page directly without making the user do it.

PROCEDURE Pgfpagerefresh1.Page1.Activate
LOCAL lok, linit
 
lok = DODEFAULT()
linit = THISFORM.GetPProp(“ilInitialEntry”)
 
*
*  handle special case. nPMID was supplied as a parameter. Otherwise it would be 0.
*    Also this is the FIRST time the page has been activated. We want to jump to
*    page 2 so Irene can check address/phone/email data again the registration form,
*    then on page 3 fill in the bBookPg field.
*
IF lInit AND thisform.nPMID > 0
   thisform.pgfpagerefresh1.ActivePage = 3
ENDIF
 
RETURN lok
ENDPROC

frmPilgrims.Pgfpagerefresh1.enabledisable
Running the NODEFAULT command causes this code to run instead of the standard class library code to run. This prevents pages from being disabled falsely.

PROCEDURE Pgfpagerefresh1.enabledisable
*
*  we need to nodefault this method to avoid execution of the following code,
*    which misbehaves because we are using edit mode for a new pilgrim rather
*    than add mode (we have to do that), and since we zap curPilgrims, this
*    code disables pages 2-5, which is no good…
*
NODEFAULT
 
*  CASE NOT EMPTY(THISFORM.ioPicklistGrid.RecordSource) ;
*       AND EOF(THISFORM.ioPicklistGrid.RecordSource) ;
*       AND INLIST(m.lcMode,”EDIT”,”DEFAULT”)
*  *******************************************************
*    *
*    *  there are no records in the grid on Page1 and we’re in 
*    *  “EDIT” or “DEFAULT” mode — all pages > 1 are disabled
*    *
*    FOR xx = 2 TO THIS.PageCount
*      IF TYPE(“THIS.Pages[m.xx].BaseClass”) = “C”
*        *
*        *  unfortunately, if you RemoveObject() a page
*        *  (i.e. user security), VFP doesn’t always
*        *  decrement .PageCount reliably, so THIS.Pages[xx]
*        *  can be .NULL., hence the IF check above
*        *
*        THIS.Pages[m.xx].Enabled = .f.
*      ENDIF
*    ENDFOR
ENDPROC

frmPilgrims.grdNav.setcustomizableresortbehaviors
During start-up configure the grid for clicking the Headers of some columns to allow sorting on those columns in descending order.

PROCEDURE Pgfpagerefresh1.Page1.grdNav.setcustomizableresortbehaviors
*
*  independent of appconfig settings, let the user sort descending.
*
THIS.ilToggleADOnColumnResort = .T.
this.ilResortOnSingleClick = .T.
 
RETURN .T.
ENDPROC

frmPilgrims.grdNav.setcolumncontrolsources
When the navigation grid is being instantiated this method connects the curPilgrims result set with the grid columns.

PROCEDURE Pgfpagerefresh1.Page1.grdNav.setcolumncontrolsources
*
*  16Nov07 added VT_dUltstart to the right column.
*
IF USED(“curPilgrims”)
   this.column1.ControlSource = “curPilgrims.PM_cLast”
   this.column2.ControlSource = “curPilgrims.PM_cFirst”
   this.column3.ControlSource = “curPilgrims.PM_cMiddle”
   this.column4.ControlSource = “curPilgrims.PM_cCity”
   this.column5.ControlSource = “curPilgrims.PM_cState”
   this.column6.ControlSource = “curPilgrims.PM_cCountry”
   this.column7.ControlSource = “curPilgrims.GP_cName”
   this.column8.ControlSource = “curPilgrims.VT_dUltstart”
ENDIF
 
this.column1.Width = 140
this.column2.Width = 130
this.column3.Width = 105
this.column4.Width = 125
this.column5.Width = 56
this.column6.Width = 100
this.column7.Width = 135
this.column8.Width = 84
 
RETURN .T.
ENDPROC

frmPilgrims.grdNav.setindextaginfo
Fill in the column headers during startup.

PROCEDURE Pgfpagerefresh1.Page1.grdNav.setindextaginfo
this.iaColumninfo(1,3) = ‘Last’
this.iaColumninfo(2,3) = ‘First’
this.iaColumninfo(3,3) = ‘Middle’
this.iaColumninfo(4,3) = ‘City’
this.iaColumninfo(5,3) = ‘State’
this.iaColumninfo(6,3) = ‘Country’
this.iaColumninfo(7,3) = ‘Group’
this.iaColumninfo(8,3) = ‘Arrival’
this.iaColumninfo(13,3) = ‘DtBirth’
 
RETURN .T.
ENDPROC

frmPilgrims.ClearCursor
Empty the curPilgrims result set local table.

PROCEDURE clearcursor
LOCAL lnSel, ln, lok
 
lnSel = SELECT()
SELECT CurPilgrims
COUNT FOR !DELETED() TO ln
IF ln > 0
   lok = .t.
   TRY 
      ZAP           &&  ZAP of our cursor works, but DELETE FROM below was leaving a row behind
   CATCH            &&    some of the time(!)
      lok = .F.
   ENDTRY
   IF !lok
      DELETE FROM curPilgrims WHERE .T.
   ENDIF
ENDIF
 
SELECT (lnSel)
RETURN (ln > 0)
ENDPROC

frmPilgrims.RetrievePilgrims
Outside of VFP you’ll probably have to handle this quite differently. Here we are working withing the curPilgrims result set temp table. You may be using some other way of managing a result set.

*– code from the page1 apply command button. now shared with txtMiddle.Customvalid, which gives new way to select a pilgrim
PROCEDURE retrievepilgrims
LPARAMETERS tcExpression
 
*
*  retain the caller’s expression so we can recycle it when calling this method
*    from cancelpilgrims()
*
this.cRetrieveExpression = tcExpression
 
*
*  make sure grdnav’s record source is set and ready to be appended to
*
SELECT curPilgrims
IF RECCOUNT(“curPilgrims”) > 0
   lok = .T.
   TRY
   ZAP
   CATCH
      lok = .F.
   ENDTRY
   IF !lok
      DELETE FROM curPilgrims WHERE .T.
   ENDIF
ENDIF
 
*
*  get connect handle
*
lnHandle = oLib.oConnectionSVC.GetSingleConnectHandle()
IF lnHandle <= 0
   =MESSAGEBOX(“Unable to connect to SQL Server”,MBEO,”Communication Problem”)
   RETURN .T.
ENDIF
 
*
*  ok, time to do the retrieve
*
lcCmd = “EXEC USP_Ret_Pilgrims “+tcExpression
_cliptext = lcCmd
lnResult = H2SQLexe(lnHandle, lcCmd, “RS_Pilgrims”)
IF lnResult <= 0
   *
   *  error message will have been reported by h2Sqlexe
   *
   RETURN .T.
ENDIF
IF !USED(“RS_Pilgrims”)
   RETURN .T.
ENDIF
IF RECCOUNT(“RS_Pilgrims”) < 1
   WAIT WINDOW “No matching Pilgrims found” timeout(1)
   USE IN RS_Pilgrims
   RETURN .T.
ENDIF
 
*
*  install result set rows in our cursor for display
*
SELECT RS_Pilgrims
lcDBF = DBF()
*
*  defend against null birthdate. Keep this code unless/until birthdt
*    becomes a required field.
*
REPLACE ALL PM_dBirth WITH {//} FOR ISNULL(PM_dBirth)
*  added support for this newly-added date 16Nov07. often this will be unknown, 
*    depending on the scope, and we return 1/1/1900 as the date when the date can not
*    be known. this prevents NULL conversion problems in VFP.
*
REPLACE ALL VT_dUltstart WITH {//} FOR TTOD(VT_dUltstart)=DATE(1900,1,1)   &&  added 16Nov07
 
SELECT curPilgrims
APPEND FROM &lcDBF   &&  insert rows, rebuild indexes
GOTO TOP
USE IN RS_Pilgrims
 
RETURN .T.
ENDPROC

frmPilgrims.DuplicateCheck
Detect suspected dupicate(s) before doing an Add operation for a new pilgrim. Very helpful.

PROCEDURE duplicatecheck
* —————————————————————–
*
*  08Jan08 cloned this code from savepilgrims() so that we can do early
*    detection of a duplicate pilgrim.  We check for an existing record
*    with same name as the one we are adding.
*    Extra sophistication will allow us to convert an Add into
*    an Edit if a duplicate is identified and the user accepts one of the
*    duplicate records as the desired one.
*
* —————————————————————–
LOCAL tnID, lcExpression, lnHandle, lnResult, lcCmd, lnDups, lcMessage, ldNow, ;
   lcPilgrimage, ldBirth
 
WAIT WINDOW “Checking for duplicate records” timeout(0.8)
 
*
*  prep parameter list for the Retrieve SP
*
lcExpression = “”
IF !EMPTY(curPilgrims.PM_cFirst)
   IF !EMPTY(lcExpression)
      lcExpression = lcExpression + “, “
   ENDIF
   lcExpression = lcExpression + “@tFirst='”+strtran(TRIM(curPilgrims.PM_cFirst),”‘”,””””)+”‘”
ENDIF 
IF !EMPTY(curPilgrims.PM_cLast)
   IF !EMPTY(lcExpression)
      lcExpression = lcExpression + “, “
   ENDIF
   lcExpression = lcExpression + “@tLast='”+strtran(TRIM(curPilgrims.PM_cLast),”‘”,””””)+”‘”
ENDIF 
IF !EMPTY(curPilgrims.PM_cMiddle)
   IF !EMPTY(lcExpression)
      lcExpression = lcExpression + “, “
   ENDIF
   lcExpression = lcExpression + “@tMiddle='”+strtran(TRIM(curPilgrims.PM_cMiddle),”‘”,””””)+”‘”
ENDIF 
lcExpression = lcExpression + “, @tScope=3 “
 
*
*  get connection handle. Actually H2SQLexe would do that for us…
*
LOCAL lnHandle, lcCmd, lnResult, lcFilenm
lnHandle     = oLib.oConnectionSVC.GetSingleConnectHandle()
IF lnHandle <= 0
   RETURN .F.
ENDIF
 
*
*  do the lookup
*
lcCmd = “EXEC dbo.USP_Ret_Pilgrims “+lcExpression
lnResult = H2SQLexe(lnHandle, lcCmd, “RS_Dup”)
DIMENSION laDups(1,2)
lnDups = 0
IF lnResult > 0 AND USED(“RS_Dup”) AND RECCOUNT(“RS_Dup”) >= 1
      
   lcMessage = ”
   ldNow = DATE()
   SELECT RS_Dup
   SCAN
      *
      *  have a dup record, maybe even 2+!
      *
      lnDups = lnDups + 1
      DIMENSION laDups(lnDups,2)
 
      *
      *  calc age of possible dup pilgrim
      *
      ldBirth = TTOD(PM_dBirth)
      IF ldBirth > ldNow
         ldBirth = DATE(YEAR(ldBirth)-100,MONTH(ldBirth),DAY(ldBirth))
      ENDIF
 
      *
      *  make birthdate into display format
      *
      DO CASE
      CASE ldBirth = DATE(1900,01,01)        &&  this is the birthdate we record when birthdate is missing
         lcPilgrimage = “Unknown”
      CASE !EMPTY(ldBirth) AND TYPE(“ldBirth”)=”D”
         lcPilgrimage = LTRIM(Str(YEAR(ldNow)-YEAR(ldBirth) ;
            – IIF(RIGHT(DTOS(ldBirth),4) > RIGHT(DTOS(ldNow),4),1,0)))
      OTHE
         lcPilgrimage = “0”
      ENDCASE
 
      *
      *  add this pilgrim to the display message
      *
      laDups(lnDups,2) = RS_Dup.PM_ID
      laDups(lnDups,1) = TRIM(RS_Dup.PM_cFirst) + TRIM(” “+RS_Dup.PM_cMiddle) ;
            + TRIM(” “+RS_Dup.PM_cLast)+CR+ ;
            IIF(PM_cGender=’F’,’Female age ‘,’Male age ‘)+lcPilgrimage+’, born ‘+DTOC(ldBirth)+CR+ ;
            TRIM(RS_Dup.PM_cCity)+”, “+TRIM(RS_Dup.PM_cState)+”, “+TRIM(RS_Dup.PM_cCountry)
   ENDSCAN
   USE IN RS_Dup
   tnID = 0
 
   *
   *  ask the user whether one of the duplicates is the person she intended
   *   
   DO FORM frmPilgrimdup WITH lnDups,laDups TO tnID
   
   IF tnID > 0    &&  tnID=0 means to continue the Add operation. duplicate is false alarm
      *
      *  here’s the tricky part–convert the Add into an edit for the Pilgrim whose PM_ID is
      *    in tnID. Our ‘Add’ uses thisform.Editaction(), which we might want to cancel.
      *    Also we need to pre-select the pilgrim whose ID is in tnID as if s/he had been
      *    chosen from Page 1
      *
      lcExpression = ‘@tPMID=’+LTRIM(STR(tnID))
      this.Retrievepilgrims(lcExpression)   &&  re-read the hPilgrims settings for (tnID)
      thisform.pilgrimreset()   &&  this will reset child cursors and call newpilgrims(),
                                &&   which will set thisform.nPMID. voila! almost finished.
      *
      *  last part of undoing the Add is reversing the effect of the Editaction() call we made and
      *    the Append Blank we did. The Append Blank was undone by ZAPping curPilgrims in 
      *    the RetrievePilgrims() method. So all that’s left is to undo the Save/Cancel buttons
      *    we’re currently showing.
      *
      ***THISFORM.SetMode(“DEFAULT”)
      this.Refresh()   &&  show the new fields we retrieved
 
      *
      *  Note 09Jan08 tried the call above to SETMODE(). Not only did it not reset the Save/Cancel
      *    buttons, but it also did not show the retrieved fields until I clicked Cancel. So the lesser
      *    evil for now until I figure out the problem is to leave the form in editaction() mode.
      *
   ENDIF
ENDIF
 
RETURN .T.
ENDPROC

FORM frmPilgrims - Code associated with the form itself

Introduction
These methods are associated with the FORM instead of being placed on the container objects (5 of them) that reside on the Tabs (VFP says Pages) of the Tab control of the form. There is a giant amount of code in the complete Pilgrims form. We are dividing all the code of the Pilgrims form into 7 popups of which this is the (main) one for the form itself. Then there are 5 popups for the code on the 5 Tabs plus one popup for the methods that are needed to perform a Save. These are spread across the main form and the Tabs of the form, so we published them together for ease of reading.

frmPilgrims.PageActivate
This gets executed every time the op moves from one Tab of the form to another. Complex and important.

PROCEDURE pageactivate
LPARAMETERS tcPage
 
*
*  This is called by pgfpagerefresh1.commonpageactivate(). All this code could be done
*    in there, but it’s easier to maintain if it is a method of the form (along with many others)
*    rather than of the pageframe.
*
*  One wrinkle in the plan–after this code executes and we Return to the caller, 
*    thisform.pgfpagerefresh1.commonpageactivate(), that code will finish up with a callback
*    like this: DODEFAULT(toPage). In other words we’re not finished with processing related to
*    the page the user wants to activate yet. There is code below that wants to change the
*    page. We defer changing pages until after the DODEFAULT() has run for THIS page. 
*    then we change pages at the end of commonpageactivate. I believe we’ll end up back here again
*    for the new page we changed to, where if we don’t do things right, we could end up in a loop. 
*
* —————————————————————————————
*
*  user must not click on a page other than page 1 until a Pilgrim has been chosen.
*    The normal way of choosing a Pilgrim, i.e. by selecting her from the grdnav, will
*    ensure that the following test evaluates .F. because selectionmade() will have set
*    thisform.nPMID to match curPilgrims.PM_ID. BUT it is possible (common) to bypass
*    selectionmade() and this code, altered 10Nov07, takes care of that by calling the new
*    pilgrimreset() method.
*
IF tcPage <> “1” AND (thisform.nPMID<=0 OR thisform.nPMID != curPilgrims.PM_ID)
   *
   *  user has not double-clicked a grdnav row, and thus has bypassed selectionmade().
   *    but if one is highlighted (or freshly inserted) we can treat it as a choice.
   *
   IF EOF(“curPilgrims”) OR EMPTY(curPilgrims.PM_ID)
      =MESSAGEBOX(“On Page 1, you must select a Pilgrim record. Then you may advance to the other pages…”,MBEO,”Select a Pilgrim”)
      RETURN -1         &&  indicate serious problem with doing a page change
   ENDIF 
   
   *
   *  here’s where we pre-select the ID. this clears the now-obsolete curVisits, curBookings, curRoom
   *    and it calls the page2 newpilgrims() method, which sets and clears synchronization properties.
   *    selectionmade() makes this call, and here’s where we match its logic.
   *
   thisform.pilgrimreset()
 
ENDIF
 
WITH thisform.pgfpagerefresh1
DO CASE
CASE tcPage = ‘2’
   * ———————————————————————————–
   *
   *  Prep for Pilgrim fields page. Controls need refreshing if we’re coming from page 1.
   *    if we are checking back from pages 3, 4 or 5 to see a Pilgrim field setting,
   *    we do not want to disturb anything…
   *
   * ———————————————————————————–
   *
   *  10Nov07 commented out the code below because the new code above has already taken care
   *    of all synchronization issues by calling newpilgrims() after deleting obsolete
   *    cursors (if any).
   *
*   DO CASE
*   CASE thisform.nPMID = 0
*      RETURN -1     &&  user has not made a choice yet. disallow page movement
*   CASE thisform.nPMID > 0
*      *
*      *  at this point we do not know whether we have visited pages 3/4/5 yet FOR THIS PILGRIM
*      *    if we have not yet been there, either we’ll have no PMID associated with these pages
*      *    or the wrong pmid. in either case, it is safe to go to newpilgrims, which will set
*      *    thisform.nVTID and thisform.nBKID to zero.
*      *  (this mod 28dec06)
*      *
*      IF thisform.nPMID <> thisform.nPage3pmid ;
*            OR thisform.nPMID <> thisform.nPage4pmid
*         *
*         *  debugging notes: if we selected a new pilgrim from page 1, selectionmade() already
*         *    called newpilgrims(), which already has set thisform.nVT_ID and .nBK_ID to 0.
*         *    what case is there in which we need to do all that again? What I think we really
*         *    need to do here is to make sure page 3 and 4 do not display data from a previous
*         *    pilgrim (which has been happening)…
*         *
*         thisform.pgfpagerefresh1.page2.ctrDEpilgrims.newPilgrims()
*      ENDIF
*   ENDCASE
 
   *
   *  just refresh so the newly-selected Pilgrim fields are shown
   *
   thisform.pgfPagerefresh1.Page2.Refresh()
   
   RETURN 0        &&  rc=zero means all’s well, allow commonpage activate to switch to toPage
   
 
 
CASE tcPage = ‘3’
   * ———————————————————————————–
   *
   *  Prep for Visits Page
   *
   * ———————————————————————————–
 
   *
   *  detect no pilgrim selected. I THINK this code was made obsolete 10Nov07 when we put in
   *    a call to newpilgrims() when thisform.nPMID = 0…no harm for now to leave it in.
   *
   IF thisform.nPMID=0
      =MESSAGEBOX(“On Page 1, you must select a Pilgrim record. Then you may”+ ;
         ” advance to the other pages…”,MBEO,”Select a Pilgrim”)
      RETURN -1
   ENDIF 
 
   *
   *  handle first or subsequent return to page 3 for existing pilgrim
   *
   IF thisform.nPMID = thisform.nPage3PMID
      *
      *  Pilgrim ID in sync. There may or may not be a Visits record for this
      *    Pilgrim, but we have at least tried to find it. No find needed here.
      *  Another way to say this is that we have already been to page 3 for this pilgrim,
      *    whether we found any Visits for him/her or not.
      *  13mar07 added calls for bookings and room. why? (a) in support of autocorrect,
      *    which requires curBookings and curRoom to be populated, even if the user has
      *    not displayed those pages. (b) because currentvisits->findvisits->newvisits
      *    was assigning thisform.nVTID and resetting thisform.nBKID to 0. I fixed that
      *    (I think), but calling currentbookings() will ensure that if thisform.nBKID was
      *    set to 0, it’ll be corrected ASAP…
      *
      IF thisform.nVTID <> curVisits.VT_ID
         .page3.ctrDEvisits.findvisits()         &&  sets thisform.nVTID via newvisits() method
         .page4.ctrDEbookings.currentbookings()  &&  find all Bookings for the selected visit, preselect current
         .page5.ctrDEroom.currentroom()          &&  find all Room assignments for preselected bookings
      ENDIF
      
      RETURN 0        &&  rc=zero means all’s well, allow commonpage activate to switch to toPage
   ENDIF
 
   *
   *  either new pilgrim or new visits row has been selected. note that both currentvisits()
   *    and findvisits() use newvisits() to prepare the curVisits cursor for displaying data.
   *    note also that newvisits() sets thisform.pgfpagerefresh1.page3.ctrDEvisits.nPMID.
   *
   *  new, tricky code added 13mar07. user quite likely has not yet gone to page 4 or 5 either,
   *    but we need to ensure curBookings/curRoom are created now. why? We added the autocorrect
   *    feature to the Bookings and Room containers. Until now, if you never went to a page there
   *    was no need to pre-charge the cursor for that page, because you couldn’t change anything
   *    without the text, combo, and edit boxes to interact with. But now, at Saveaction() time,
   *    autosave might need to adjust a booking or room assignment start or end date, so those
   *    cursors must already exist if there’s any chance you’ll change a visit date.
   *
   IF thisform.nPage3PMID = 0
      *
      *  have new pilgrim selection
      *
      ln = .page3.ctrDEvisits.currentvisits()      &&  find all Visits for the new Pilgrim, highlight current one
   ELSE
      *  container has info about some previous pilgrim, now obsolete and in need of refreshing
      ln = .page3.ctrDEvisits.findvisits()      &&  find all Visits for the new Pilgrim, highlight current one
   ENDIF
   *
   *  create the extra cursors, as explained above
   *
   ln = .page4.ctrDEbookings.currentbookings()  &&  find all Bookings for the selected visit, preselect current
   ln = .page5.ctrDEroom.currentroom()  &&  find all Room assignments for preselected bookings
 
   RETURN 0        &&  rc=zero means all’s well, allow commonpage activate to switch to toPage
 
 
CASE tcPage = ‘4’
   * ———————————————————————————–
   *
   *  Prep for Bookings Page. note that the old, wrong logic is in storage() in case this new
   *    logic gives trouble (but I’m sure it’s solid–16Jan06)
   *
   * ———————————————————————————–
   *
   *  detect no pilgrim selected. I THINK this code was made obsolete 10Nov07 when we put in
   *    a call to newpilgrims() when thisform.nPMID = 0…no harm for now to leave it in.
   *
   IF thisform.nPMID=0
      *
      *  no pilgrim has been selected
      *
      =MESSAGEBOX(“On Page 1, you must select a Pilgrim record. Then you may”+ ;
         ” advance to the other pages…”,MBEO,”Select a Pilgrim”)
      RETURN -1
   ENDIF 
 
   *
   *  test for pilgrim new to page 4 OR
   *    the visit is not for this pilgrim OR
   *    the visit is not in sync with this bookings page OR
   *    there is no visit
   *
   IF thisform.nPMID <> thisform.nPage4PMID OR ;
         thisform.nPMID <> thisform.nPage3PMID OR ;
         thisform.nVTID <> thisform.nPage4VTID OR ;
         thisform.nVTID=0
 
      IF thisform.nPMID <> thisform.nPage3PMID
         *
         *  user has skipped over page 3, so we need to pre-select a visit here, if possible
         *
         ln = .page3.ctrDEvisits.currentvisits()      &&  find all Visits for the new Pilgrim, highlight current one
         IF ln = 0
            *
            *  no business going to page 4–no visits exist or can be autoselected
            *
            =MESSAGEBOX(“Please select or create a Visit for this Pilgrim”, ;
               MBEO,”Visit info needed”)
            RETURN 3
         ENDIF
      ENDIF
 
      *
      *  test whether the current visit is in sync with the bookings page. if not, we’ll
      *    have to pick one
      *
      IF thisform.nVTID <> thisform.nPage4VTID
         *
         *  update page 4 with our best guess as to booking to preselect. note that if ln=0,
         *    it means that we didn;t find a booking (yet) so there’s no need to try to locate
         *    its room assignment–couldn’t exist yet.
         *
         ln = .page4.ctrDEbookings.currentbookings()  &&  find all Bookings for the selected visit, preselect current
      
         *
         *  new, tricky code added 13mar07. user quite likely has not yet gone to page 5 either,
         *    but we need to make sure curRoom is created now. why? On this day we added the autocorrect
         *    feature to the Bookings and Room containers. Until now, if you never went to a page there
         *    was no need to pre-charge the cursor for that page, because you couldn’t change anything
         *    without the text, combo, and edit boxes to interact with. But now, at Saveaction() time,
         *    autosave might need to adjust a booking or room assignment start or end date, so those
         *    cursors must already exist…
         *
         IF ln != 0
            ln = .page5.ctrDEroom.currentroom()  &&  find all Room assignments for preselected bookings
         ENDIF
      ENDIF
   ENDIF
 
   *
   *  we have been to page 4 before for this same visit/pilgrim. nothing needed
   *
   .page4.ctrDEbookings.setcaption()  &&  Visits data may have been edited so redisplay
   RETURN 0
 
 
CASE tcPage = ‘5’
   * ———————————————————————————–
   *
   *  Prep for Room Assignments Page
   *
   * ———————————————————————————–
   *
   *  detect no pilgrim selected. I THINK this code was made obsolete 10Nov07 when we put in
   *    a call to newpilgrims() when thisform.nPMID = 0…no harm for now to leave it in.
   *
   IF thisform.nPMID=0
      *
      *  no pilgrim has been selected
      *
      =MESSAGEBOX(“On Page 1, you must select a Pilgrim record. Then you may”+ ;
         ” advance to the other pages…”,MBEO,”Select a Pilgrim”)
      RETURN -1
   ENDIF 
 
   *
   *  test for pilgrim new to page 5 OR
   *    pilgrim not new but visit has changed OR
   *    there is no visit OR
   *    booking has changed OR
   *    there is no booking
   *
   IF thisform.nPMID <> thisform.nPage5PMID OR ;
         thisform.nVTID <> thisform.nPage5VTID OR ;
         thisform.nVTID = 0 OR ;
         thisform.nBKID <> thisform.nPage5BKID OR ;
         thisform.nBKID = 0
      *
      *  test whether the visit is selected for this pilgrim
      *
      IF thisform.nPMID <> thisform.nPage3PMID
         *
         *  user has skipped over page 3, so it needs to adjust its visit to this new pilgrim choice
         *
         ln = .page3.ctrDEvisits.currentvisits()      &&  find all Visits for the new Pilgrim, highlight current one
         IF ln = 0
            *
            *  no business going to page 5–no visits exist or can be autoselected
            *
            =MESSAGEBOX(“Please select or create a Visit for this Pilgrim”, ;
               MBEO,”Visit info needed”)
            RETURN 3
         ENDIF
      ENDIF
      *
      *  test whether the booking is selected for this pilgrim
      *    NOTE: I believe either of the following tests will work, which suggests we are
      *    maintaining more flags than we need to (10Nov07 not so sure about the above comment.
      *    Note in newvisits() that we set thisform.nP
      *
      IF thisform.nVTID <> thisform.nPage4VTID OR thisform.nVTID=0
      ***IF thisform.nPMID <> thisform.nPage4PMID
         *
         *  user has skipped over page 4, so it needs to adjust its booking to this visit choice
         *
         ln = .page4.ctrDEbookings.currentbookings()  &&  find all Bookings for the selected visit, preselect current
         IF ln = 0
            *
            *  no business going to page 5–no bookings exist or can be autoselected
            *
            =MESSAGEBOX(“Please select or create a Booking for this Pilgrim”, ;
               MBEO,”Booking info needed”)
            RETURN 4
         ENDIF
      ENDIF
 
      *
      *  update page 5 with our best guess as to best room assignment to preselect
      *
      ln = .page5.ctrDEroom.currentroom()      &&  find Rooms for the Booking, highlight current one
   ENDIF
 
   *
   *  we have been to page 5 before for this same booking/visit/pilgrim. nothing needed
   *
   .page5.ctrDEroom.setcaption()  &&  Visits or Booking data may have been edited so redisplay
 
ENDCASE
ENDWITH
 
*
*  user may freely click onto all pages once all are in sync–no navigation, no pending saves.
*
RETURN 0
ENDPROC

frmPilgrims.SaveAction
Called to be the general manager of performing a Save. With 4 tables in parent-child relationships there is a lot of (critical!) detail to manage.

PROCEDURE saveaction
LOCAL lok
lok = .T.
 
*
*  thisform.saveaction()  Note no callback here. We have no buffered local tables or views and
*    no remote views that are updatable. All I/O we do is handled by the four methods devoted
*    to saving the parent (hPilgrims), child (hVisits), grandchild (hBookings), and great-
*    grandchild (hBookingsRooms) record(s).
*  the save logic for these tables is included in the container for each table.
*
*  Note that most of the fancier logic here is appropriated from the std VMP Saveaction,
*    like turning the mouse pointer to an hourglass, which we’ll do first.
*
loMP = CREATEOBJECT(“cusPushPopMousePointers”,.t.)
 
*  
*  determine what mode we were in, following VMP, though we’ll probably never need to know…
*
PRIVATE pcMode
pcMode = THIS.GetMode()
IF pcMode = “D”
   =MESSAGEBOX(“Tell your programmer that “+CHR(13)+CHR(13)+ ;
      “the Saveaction method of the main Pilgrims data entry form “+ ;
      “tried to go to the Saveaction method when in Default mode.”,MBEO,”Internal Problem”)
   RETURN .F.
ENDIF 
 
*
*  call a shell method allowing you to insert code here 
*  without having to subclass this method
*
IF NOT THIS.SaveBeforeAnything()
  pc_SaveActionDebugging_SectionThatFailed = “The SaveAction() call to THIS.SaveBeforeAnything() failed”
  THIS.SaveAfterFailure()
  THIS.SaveMessageOnFailure()
  RETURN .F.
ENDIF
 
llLockScreenHere = THIS.LockScreenHere()
 
*
*  13mar07 new feature–presave auto correction. We had intended to do auto correction
*    in the savebookings() and saveroom() methods of the respective containers, but
*    SQL Server triggers will fire before those methods can act. e.g. if you shorten the 
*    visit, the savevisits method gets caught by the trigger before the savebookings()
*    method ever gets called.
*  here, we’ll scan through bookings and rooms for the current visit and invoke the auto-
*    correct method of each to get things as right as can be before we call the first
*    save…() method. BUT WAIT! that’s not enough. We also need to determine whether to try
*    a forward save (parents before children) which applies when there are added rows and/or
*    expanded dates. We must do a backwards save in the opposite case that there are no adds
*    and a date has been contracted in a parent record (e.g. visit start date decreased or
*    booking end date decreased).
*
WITH thisform.pgfpagerefresh1
lok = .T.
thisform.lSaveDirection = SAVE_FORWARD   &&  note that if either autocorrect method returns ‘backward’, we switch
IF lok
   *
   *  sets savedirection property
   *
   lok = .page4.ctrDEbookings.autocorrectbookings()
ENDIF 
IF lok
   *
   *  also sets savedirection property. we could get into trouble if there are 
   *    blocks of code that insist on opposite save directions and override each other.
   *    so far we don’t detect a direction reversal and warn or refuse to go ahead…
   *
   *  one example of how this could happen: make visit start earlier. this is expansive and
   *    we want to save forward (visit, then booking, then room). but also edit departure
   *    to be earlier. this is restrictive and we want to save backward (room then booking then visit).
   *    to do this right, Pat/Irene should save after each date change. else one of the date changes
   *    will cause a failure in an update trigger of the database.
   *
   lok = .page5.ctrDEroom.autocorrectroom()
ENDIF
 
*
*  now we know whether to try forward or backward save according to dates edits. BUT
*    we also need to confirm no new Pilgrim or Visits row, which would make a backward
*    save impossible (because the parent record would not have been added when we try
*    to save the child record). 
*  [We COULD detect a setting of SAVE_BACKWARD here in thisform.lSavedirection and, if found, warn
*    the user that a failure is inevitable. That should be rare, however.]
*
IF lok AND (thisform.nPMID < 0 OR thisform.nVTID < 0)
   thisform.lSavedirection = SAVE_FORWARD   &&  adding a parent record so forward is required
ENDIF
 
*
*  perform the saves down through the generations to prevent problems with
*    added rows. Each has a foreign key to the parent and depends on the
*    parent’s save. So if we added a visit, a booking and a room assignment
*   for pilgrim 001, each generation will receive a primary key from SQL Server
*    in time for it to be used as a foreign key for its child.
*
IF thisform.lSavedirection = SAVE_FORWARD
***WAIT WINDOW “Saveaction() forwards” timeout(1)
   IF lok AND thisform.lModpilgrims   &&  hPilgrims row was edited or added
      lo = thisform.oPMparamblock
      lok = .page2.ctrDEpilgrims.savepilgrims(lo)
   ENDIF
   IF lok AND thisform.lModvisits     &&  hVisits row was edited or added
      lo = thisform.oVTparamblock
      lok = .page3.ctrDEvisits.savevisits(lo)
   ENDIF
   IF lok AND thisform.lModbookings   &&  hBookings row was edited or added
      lo = thisform.oBKparamblock
      lok = .page4.ctrDEbookings.savebookings(lo)
   ENDIF
   IF lok AND thisform.lModrooms      &&  hBookingsRooms row was edited or added
      lo = thisform.oBRparamblock
      lok = .page5.ctrDEroom.saveroom(lo)
   ENDIF
ELSE
   *
   *  this save is the BACKWARD one, i.e. starting with the child records. we need this
   *    approach if parent records are in place and we are shortening time intervals,
   *    either decreasing an End date or increasing a Start date. Prevents a trigger
   *    from aborting a save.
   *
***WAIT WINDOW “Saveaction() backwards” timeout(1)
   IF lok AND thisform.lModrooms      &&  hBookingsRooms row was edited or added
      lo = thisform.oBRparamblock
      lok = .page5.ctrDEroom.saveroom(lo)
   ENDIF
   IF lok AND thisform.lModbookings   &&  hBookings row was edited or added
      lo = thisform.oBKparamblock
      lok = .page4.ctrDEbookings.savebookings(lo)
   ENDIF
   IF lok AND thisform.lModvisits     &&  hVisits row was edited or added
      lo = thisform.oVTparamblock
      lok = .page3.ctrDEvisits.savevisits(lo)
   ENDIF
   IF lok AND thisform.lModpilgrims   &&  hPilgrims row was edited or added
      lo = thisform.oPMparamblock
      lok = .page2.ctrDEpilgrims.savepilgrims(lo)
   ENDIF
ENDIF
 
IF lok
   *
   *  next, call a method where you can place code to
   *  execute now, without having to subclass this 
   *  method
   *  …please read the note entitled “Messaging to the user 
   *  subsequent to <Save> success” in the header comments of
   *  this method
   *  
   THIS.SaveAfterSuccess()
   THIS.SetMode(“DEFAULT”)
 
   *
   *  continue on with VMP default behaviors
   *
   THIS.UpdateFormOnSave()
   THIS.LockScreenHere(m.llLockScreenHere)
ELSE
   *
   *  we need to undo any mods we made, by requerying any
   *    curVisits, curBookings and/or CurRoom to display the orig values
   *  here, we only know that one or more of the save methods failed.
   *    in fact if the saveroom failed, we don’t have all the mods
   *    (and don’t WANT all the mods) packaged in a transaction,
   *    so it’s quite likely that some updates/inserts went through ok.
   *    it does no harm to ask all the containers of cursors that were
   *    altered to restore the cursors, some reflecting succesful mods,
   *    others reverting to the original values.
   *
   IF thisform.lModpilgrims AND curPilgrims.PM_ID < 0
      .page2.ctrDEpilgrims.cancelpilgrims()   &&  hPilgrims row was edited or added
   ENDIF
   IF thisform.lModvisits
      .page3.ctrDEvisits.cancelvisits()
   ENDIF
   IF thisform.lModbookings
      .page4.ctrDEbookings.cancelbookings()
   ENDIF
   IF thisform.lModrooms
      .page5.ctrDEroom.cancelroom()
   ENDIF
 
   *
   *  “normal” failure — call a shell method allowing you 
   *  to put code to execute here without having to 
   *  subclass this method
   *
   THIS.SaveAfterFailure()
   THIS.SetMode(“DEFAULT”)
 
   THIS.UpdateFormOnCancel()
   THIS.LockScreenHere(m.llLockScreenHere)
ENDIF
ENDWITH 
 
RETURN lok
ENDPROC

frmPilgrims.SaveAfterSuccess
Called when the Save has completed successfully.

PROCEDURE saveaftersuccess
 
*
*  no more Save items pending…
*
thisform.lModpilgrims = .f.
thisform.lModvisits = .f.
thisform.lModbookings = .f.
thisform.lModrooms = .f.
 
*
*  …but as of 15jan10 I see the need for one cleanup–if the user’s group was changed
*    and the selection crieria for the grdnav on page1 included group name, then the pilgrim
*    really ought to diappear from the screen, meaning that the selection criteria need to be
*    reapplied. I tried doing that in selVisits, but at that binding time, the actual Save had not
*    been performed, so it was ineffective… 
*
IF THIS.nGPID != curVisits.VT_GPID
   IF !EMPTY(thisform.cSelectionExpression)
      *
      *  call SP USP_Ret_Pilgrims and produce the curPilgrims cursor from the generated lcExpression
      *
      lnPMID = curPilgrims.PM_ID   &&  retain ID of the pilgrims we’re working on
      *
      *  this will shift the curPilgrims record pointer
      *
      thisform.pgfpagerefresh1.Page2.ctrDEPilgrims.Retrievepilgrims(thisform.cSelectionExpression)
      *
      *  try to re-position on our pilgrim (may legitimately fail)
      *
      SELECT curPilgrims
      LOCATE FOR PM_ID = lnPMID
      IF !FOUND() AND ATC(“@TGPID”,thisform.cSelectionExpression) > 0
         =MESSAGEBOX(“The pilgrim whose group afilliation you changed may no longer “+ ;
            “be visible on Page 1. This will happen if a different group (i.e. her old group) “+ ;
            “is being displayed.”, MBO, “Please Note”)
      ENDIF
      thisform.pgfpagerefresh1.page1.grdnav.Refresh()
   ENDIF
ENDIF
 
 
RETURN DODEFAULT()
ENDPROC

frmPilgrims.SaveAfterFailure
Comments

PROCEDURE saveafterfailure
*
*  no more Save items pending…
*
thisform.lModpilgrims = .f.
thisform.lModvisits = .f.
thisform.lModbookings = .f.
thisform.lModrooms = .f.
 
RETURN DODEFAULT()
ENDPROC

frmPilgrims.CancelAfterSuccess
Called when the op has clicked the Cancel button and the Cancel has worked.

PROCEDURE cancelaftersuccess
*
*  test whether Pilgrims properties need refreshing (age, button backcolors)
*
IF thisform.lModPilgrims
   thisform.pgfpagerefresh1.page2.ctrDEpilgrims.refreshpilgrims()
ENDIF
 
*
*  no more Save items pending…
*
thisform.lModpilgrims = .f.
thisform.lModvisits = .f.
thisform.lModbookings = .f.
thisform.lModrooms = .f.
 
ln = thisform.pgfpagerefresh1.ActivePage
DO case
CASE ln = 5
   *
   *  because our curRoom cursor is not buffered, we need to rebuild it after a 
   *    cancel.
   *
   SELECT (thisform.pgfpagerefresh1.page5.ctrDERoom.grdDERoom.RecordSource)
   thisform.pgfpagerefresh1.page5.ctrDERoom.findRoom()
 
CASE ln = 4
   *
   *  because our curBookings cursor is not buffered, we need to rebuild it after a 
   *    cancel.
   *
   SELECT (thisform.pgfpagerefresh1.page4.ctrDEBookings.grdDEBook.RecordSource)
   thisform.pgfpagerefresh1.page4.ctrDEBookings.findBookings()
 
CASE ln = 3
   SELECT (thisform.pgfpagerefresh1.page3.ctrDEVisits.grdNav.RecordSource)
   thisform.pgfpagerefresh1.page3.ctrDEVisits.Refresh()   &&  since we’re not calling any other
                                                          &&    method that would refresh
 
*
*  new experimental code 20mar09. the cancelpilgrims() method was not being used
*    after a user Cancel selection. The result was that we were seeing text in the 
*    memo fields that had not been saved to SQL Server, but that was still in the
*    curPilgrims memo fields. By calln cancelpilgrims (which has been updated as well)
*    we now re-retrieve the contents of curPilgrims, and re-point to the currently-
*    seleted pilgrim.
*
CASE ln = 2
   SELECT curPilgrims
   thisform.pgfpagerefresh1.page2.ctrDEPilgrims.cancelpilgrims()
 
ENDCASE
   
RETURN DODEFAULT()
ENDPROC

frmPilgrims.PilgrimReset
Comments

*– this is called from selectionmade and from pageaction when selectiomade has been bypassed. clears cursors, etc.
PROCEDURE pilgrimreset
LPARAMETERS tnPage
LOCAL lok
 
IF PCOUNT() < 1
   tnPage = 2
ENDIF
 
*
*  on a new pilgrim selection we need to reset the flag fields, such as 
*    thisform.nVTID, the page synchronization fields, such as
*    thisform.nPage3PMID, and the cursors. SEE BELOW FOR FLAGS CHART.
*
*  10Nov07 I created this method so that pageactivate() could have a 
*    standardized way of doing what the grdnav.selectionmade() method
*    does when a new pilgrim is selected. this is needed because Irene
*    often manages to bypass the selectionmade() method. Here’s how:
*    (1) do a search that narrows down the selection to one pilgrim,
*    like lastname=Gun, first name=Jo. Gets only me. Then just click on 
*    page 3. Selectionmade will only fire if you click on the name in
*    the grdnav.
*
*  when pageactivate fires, it’ll see that thisform.nPMID is out of step
*    with curPilgrims.PM_ID and it can use this method to bring things
*    back into proper alignment
*
*  added tnPage parameter 4Jan08 so we can call clearcursor from the Add 
*    Visits/Bookings buttons on the page 3 and 4 containers. Until this
*    was implemented we were getting spuroius warnings about previous
*    orphaned bookings when adding a visit for someone with a previous
*    visit in the same season. That was because curBookings has already
*    been loaded with info about the previous booking. When we add a new
*    visit, it appeared that the old booking data was mis-dated booking 
*    data for this new visit.
*
lok = .T.
IF tnPage <= 2  &&  called from selectionmade, pageactivate, cmdApply.click–reset visits, bookings, room
   thisform.pgfpagerefresh1.page3.ctrDEvisits.clearcursor()
   *
   *  newpilgrims will reset the retained ID flags we depend on for synchronization.
   *
   lok = thisform.pgfpagerefresh1.page2.ctrDEpilgrims.newPilgrims()
ENDIF
IF tnPage <= 3  &&  called from visits (page 3) or pilgrims, so reset bookings and room
   thisform.pgfpagerefresh1.page4.ctrDEbookings.clearcursor()
ENDIF
IF tnPage <= 4  &&  called from bookings (page 4) or a parent, so reset room
   thisform.pgfpagerefresh1.page5.ctrDEroom.clearcursor()
ENDIF
 
RETURN lok
 
*   FLAG                            NewPilgrims NewVisits   NewBookings NewRoom
*thisform.nPMIDset
*thisform.nVTID0set
*thisform.nBKID00set
*
*thisform.nPage3PMID0set
*thisform.nPage4PMID00set
*thisform.nPage5PMID000set
*
*thisform.nPage4VTID00set
*thisform.nPage5VTID000set
*
*thisform.nPage5BKID000set
*
 
*  Note that ‘0’ above means ‘is set to 0 in the method’
*  ‘set’ means value is assigned from the cursor, e.g. from curVisits.VT_ID
ENDPROC

frmPilgrims.Load
Called as the form is being created, first thing before objects are instantiated. We use this to create internal tables (using the built-in file systems of VFP) that will be used as result sets to populates= grids, lists and combo boxes.
     To do a rewrite of Huware the new author(s) will need to work out how to save and use result sets from queries, since most modern languages have their own ways of presenting result sets to the programmer.

PROCEDURE Load
 
*
*  Need result sets of all Facilities we can assign someone to, AND
*    all Kitchens that can be associated with any of those facilities.
*    This must include Private, eating at Savage, or Auxilliary, eating
*    at MPR.
*  At first we tried this in shellrequerymainviewalias(). But our problem
*    was that even though we set the combo controlsource there, we found that
*    it remained or became blank. Only solution we found is to create the
*    cursors before the controls’ inits fire, so they can properly bind the
*    rowsource and the conrolsource. Otherwise not only is the controlsource
*    becoming un-set, but the interactivechange method can’t put the form
*   into edit mode because there’s no controlsource. This solves it all.
*
 
*
*  get connection handle. Actually H2SQLexe would do that for us…
*
LOCAL lnHandle, lcCmd, lnResult, lcFilenm
lnHandle     = oLib.oConnectionSVC.GetSingleConnectHandle()
IF lnHandle <= 0
   DODEFAULT()
   RETURN .F.
ENDIF
 
lok = DODEFAULT()
 
*
*  Populate combo boxes–retrieve Facilities we can book into (probably all of them)
*
lcCmd = “EXEC dbo.USP_Ret_Facilities @tScope = ‘ALL'”
lnResult = H2SQLexe(lnHandle, lcCmd, “RS_Facilities”)
IF lnResult <= 0 ;
      OR !USED(“RS_Facilities”) ;
      OR RECCOUNT(“RS_Facilities”) < 1
   =MESSAGEBOX(“Unable to retrieve Facilities table from the database”,MBEO,”USP_RET_Facilities failure”)
   DODEFAULT()
   RETURN .F.
ENDIF
 
*
*  retrieve Groups for use in combo box on Visits page
*
lcCmd = “EXEC dbo.USP_Ret_Groups”
lnResult = H2SQLexe(lnHandle, lcCmd, “tmpGroups”)
IF lnResult <= 0 OR !USED(“tmpGroups”)
   =MESSAGEBOX(“Unable to retrieve Groups table from the database”,MBEO,”USP_RET_Groups failure”)
   DODEFAULT()
   RETURN .F.
ENDIF
SELECT tmpGroups
lcDBF = DBF()
COPY STRUCTURE TO curGroups
IF !USED(“curGroups”)
   USE curGroups IN 0
ENDIF
SELECT curGroups
INSERT INTO curGroups (GP_cName, GP_ID, GP_cNotes, GP_lActive) ;
   VALUES (‘(no Group)’,0,”,0)
APPEND FROM &lcDBF
GOTO TOP
 
 
*
*  retrieve Kitchens we can assign Pilgrims to (probably all of them)
*
lcCmd = “EXEC dbo.USP_Ret_Kitchens”
lnResult = H2SQLexe(lnHandle, lcCmd, “RS_Kitchens2”)
IF lnResult <= 0 ;
      OR !USED(“RS_Kitchens2”) ;
      OR RECCOUNT(“RS_Kitchens2”) < 1
   =MESSAGEBOX(“Unable to retrieve Kitchens table from the database”,MBEO,”USP_RET_Kitchens failure”)
   DODEFAULT()
   RETURN .F.
ENDIF
 
*
*  our extra “None” row to add to the choices presented to the user.
*
CREATE CURSOR RS_Kitchens1 (KN_cDescr c(20) NOT NULL, KN_ID int NOT NULL, KN_nMealrate Y NOT NULL, KN_nDisporder int NOT NULL)
INSERT INTO RS_Kitchens1 (KN_cDescr, KN_ID, KN_nMealrate, KN_nDisporder) ;
   VALUES ;
   (‘None’, 0, 0, 999)
 
*
*  merge the Kitchens rows with the “None” row for use in our combo box
*
SELECT * FROM RS_Kitchens2 ;
UNION ;
Select * FROM RS_Kitchens1 ;
   ORDER BY KN_nDisporder ;
   INTO CURSOR RS_Kitchens
 
*
*  close the two intermediate cursors. won’t need ’em again
*
USE IN RS_Kitchens1
USE IN RS_Kitchens2
 
*
*  NEWS FLASH! The real problem with navigation grids not firing their selectionmade()
*    methods was not caused by late supplying of the recordsource, but instead was caused
*    by the form not having its icMainalias set. Odd, because we do not actually NEED VMP
*    to do anything with this table, but it is required. I tried putting in curVisits,
*    because these cursors created here in Load are the only tables in existence at this.init()
*    binding time. There’s no DE needed in this form. So take the following comments to
*    be somewhat misleading, written before I filled in icMainalias. Nonetheless, this
*   approach of not changing the recordsource of the grids looks clean to me…
*
 
*
*  Originally we worked directly from RS_Pilgrims, the result set created in the Apply.Click()
*    method of ctrSelPilgrims on page 1 of the form. BUT now we want to add indexes so the columns
*    are sortable. Best way to do this is in a file that is not replaced each time Apply is clicked.
*    So now we create our temp table here, index it, then ZAP and APPEND from RS_Pilgrims each
*    time Apply is clicked. This is less rebuilding for the grdnav as well, because we do not
*    keep changing its recordsource.
*
*  modified 15dec14 to include PM_lVisible and PM_lCform. note that PM_lVisible is not (yet) used
*    anywhere on this form…
*
CREATE CURSOR CurPilgrims (PM_ID I, PM_cFirst C(25), PM_cMiddle C(25), PM_cLast C(25), ;
   PM_cNicknm C(25), PM_cCity C(30), PM_cState C(10), PM_cCountry C(25), ;
   PM_cAddr1 C(40), PM_cAddr2 C(40), PM_cAddr3 C(40), PM_cZCode C(10), ;
   PM_cGender C(1), PM_dBirth D, PM_cPhone C(25), PM_cPhone2 C(25), PM_cCell C(25), ;
   PM_cEmail C(50), PM_mNotes C(250), PM_mMednotes C(250), PM_mHKnotes C(250), PM_mRegnotes C(250), ;
   PM_mRelated C(250), PM_mOccup C(250), GP_cName C(25), VT_dUltstart D, PM_lVisible I, PM_lCform I, ;
   PM_cIDcreate C(20), PM_dCreate D, PM_cIDupdate C(20), PM_dUpdate D)
*  NOTE: Index tag creation takes place in this.pgfpagerefresh1.page1.grdnav.createindextags()
*  NOTE2: Put the code back here because I can’t get Resort to work…some problem binding events
*    in grdnav.init -> grdnav.bindevents(), I think.
SELECT curPilgrims
INDEX on UPPER(PM_cFirst+PM_cLast) TAG First
INDEX on UPPER(PM_cLast+PM_cFirst) TAG Last
INDEX on UPPER(PM_cMiddle+PM_cLast) TAG Middle
INDEX on UPPER(PM_cCity+PM_cLast) TAG City
INDEX on UPPER(PM_cState+PM_cCity+PM_cLast) TAG State
INDEX on UPPER(PM_cCountry+PM_cLast) TAG Country
INDEX on GP_cName TAG Group
INDEX on DTOS(VT_dUltstart)+PM_cLast TAG Arrival
INDEX on DTOS(PM_dBirth)+PM_cLast+PM_cFirst TAG Dtbirth
SET ORDER TO Last
 
*
*  now we need to solve a problem with the grids on pages 3 and 4. VFP/VMP do not seem
*    comfortable with grdbase or grdpicklist/grdnavigate instances in which the rowsource
*    is not supplied during Init processing, but is filled in later. There may be some dependency
*    I haven’t found yet, like maybe icMainalias() is required, but for now I’m changing horses.
*  In the new plan, the rowsource for the grid is pre-assigned as being CurVisits, an updatable
*    cursor we’ll create here. Then will append into it from any cursor that comes back from
*    SQL Server. For Visits, it’ll be readonly, and for Bookings it’ll be a read-write grid,
*    with table buffering.
*  Note: 5June06 removed this code: “, VT_nFirstmeal I NULL, VT_nLastmeal I NULL”
*  16jul07 added bizarre field VT_dOrigUltdepart. This is used in 2 places: ctrDEVisits.newvisits()
*    where we assign it the value of the VT_dUltdepart. It will never be overwritten.
*    We refer to it in ctrDEBookings.Autocorrectbookings() to see whether the BK_dPrend needs to
*    be extended (a) because it HAD matched VT_dUltdepart, and (b) now it is being left behind
*    (because VT_dUltdepart was extended). whew!
*
*  18nov07 added VT_dOrigUltstart for use in ctrDEBookings.autocorrevtbookings() so we can make
*    a booking start earlier if its old start date=VT_dOrigUltstart and the new VT_dUltstart is
*    earlier OR (is later AND is earlier than BK_dEnd).
*
*  17jun25 removed VT_lExtlastday in favor of the correct new field BK_lExtlastday
*
CREATE CURSOR CurVisits (VT_ID I, VT_PMID I, VT_FAIDreq I, VT_dUltstart D NULL, ;
   VT_dUltdepart D NULL, VT_dOrigUltstart D NULL, VT_dOrigUltdepart D NULL, ;
   FA_cCode C(10) NULL, VT_lAdd L, ;
   VT_dPendarr D NULL, VT_dApproxarr D NULL, VT_dApproxdep D NULL, ;
   VT_lPending I NULL, VT_lArrived I NULL, VT_lRegist I NULL, VT_lComplete I NULL, ;
   VT_lHidden I NULL, VT_lNobill I NULL, VT_nBbookpg I NULL, ;
   VT_lReqsgl I NULL, VT_lReqdbl I NULL, VT_lSnores I NULL, VT_lLsleep I NULL, ;
   VT_lLongTermVisa I NULL, ;
   VT_cArrtime C(20) NULL, VT_mNotes M NULL, VT_mMednotes M NULL, VT_mPendnotes M NULL, ;
   VT_mRoomnotes C(250) NULL, VT_mReqnotes C(250) NULL, VT_GPID I, VT_cIDcreate C(20) NULL, ;
   VT_dCreate D NULL, VT_cIDupdate C(20) NULL, VT_dUpdate D NULL)
 
*
*  set up Bookings cursor. This is essential to our design, since it will allow the user to
*    modify multiple rows of the Bookings table at once, using table buffering.
*  NOTE: tried to change to a CREATE TABLE, but I couldn’t get around the errors, to
*    the effect that there is an invalid field in the field list. There are fields
*    with names > 10 chars, and these would be illegal if no database was open. Now
*    here’s the odd part. dbused(“H2″) returns .T. but DBC() returns ”. The former implies
*    the database is open and the latter indicates it’s not. The error message reinforces the
*    idea that it’s not open. So I gave up. This means we need to add a column to the cursor
*    we set to indicate whether a row was added or edited. Still experimenting…
*  22Dec2006 added the BK_dPrEnd for projected ending dates.
*
*lcFilenm = oApp.tempfilenm()
*thisform.cTempfile = lcFilenm
*IF !DBUSED(“h2”)
*   OPEN DATABASE H2
*ENDIF
*lcDBC = DBC()
*lcPath = LEFT(lcDBC,ATC(“H2”,lcDBC)-1)
*lcFile = lcPath+”curBookings.DBF”
*
*  note 13mar07 added VT_dUltdepart to curBookings, which will contain a copy of the (original)
*    value of VT_dUltdepart, which we’ll use for auto-correcting BK_dPrEnd. If the user alters
*    VT_dUltdepart, then curVisits will reflect that edit and curBookings will not…useful.
*
*  21feb12 added BK_lNoresFee new field at Trust request
*
*  27may25 added field BK_lExtLastDay, In error we had noted this extra last day charge
*    as a field in hVisits, VT_lExtLastDay, which is no good because when you do a select
*    with visit the parent and booking the child, this fee will apply to EVERY booking in
*    the visit that has the billing flag on. Rare issue but not correct; We want to know
*    for a partiular single booking (at a time) did the pilgrim agree to pay for an extra day
*    for the privilege of hanging around during the day without giving up the room at 9:30am?
*
CREATE CURSOR curBookings (BK_ID I, BK_VTID I, BK_KNID I, BK_FAIDdenorm I, ;
   BK_dStart D NULL, BK_dEnd D NULL, BK_dPrEnd D NULL, BK_nFirstmeal I NULL, BK_nLastmeal I NULL, ;
   BK_cArrtime C(20) NULL, BK_lOppgend I NULL, BK_lNoresFee I NULL, ;
   BK_lExtLastDay I NULL, BK_lStart I NULL, BK_mNotes C(250) NULL, ;
   BK_lMod L, BK_lAdd L, BK_lDel L, BK_cIDupdate C(20), BK_dCreate D, ;
   FA_cCode C(10) NULL, FA_nBookdays I NULL, ;
   FA_lBook I,FA_lBill I,FA_lPrimary I,FA_lSecondary I, VT_dUltdepart D)
 
SELECT curBookings
INDEX on DTOS(BK_dStart) DESCENDING TAG Date
 
*
*  reopen the table and set it up for table buffering
*  NOTE: If there’s no database open, why doesn’t this bomb?
*  FURTHER NOTE: this bombs with illegal field complaint, presumably because f flds with names > 10 chars,
*    when DBUSED(“H2”) returns .T., so why can;t the Create Table put curBookings into the H2 database and
*    therefore allow long field names?
*
*USE c:\temp\curbookings EXCLUSIVE
*=CURSORSETPROP(“Buffering”,5,”curBookings”)
*
*  note that 13mar07 we are adding BK_dStart, BK_dEnd and BK_dPrEnd to the curRoom cursor.
*    since these are pre-edit copies of bookings fields, by the time we save, the new values
*    of these fields will be seen in curBookings.BK_Dxxx and the original values will be seen
*    in this cursor. Useful for the new auto-correct feature.
*
CREATE CURSOR curRoom (BR_ID I,BR_dStart D,BR_dEnd D,BR_lMod L,BR_lAdd L,BR_lDel L, ;
RM_cCode C(10),RM_cCode3 C(3),RM_ID I,RM_cGender C(1),RM_nBeds I, RM_FAID I, RM_cIDupdate C(20), ;
RM_dUpdate D, FA_cCode C(10), FA_nBookdays I, ;
BK_ID I, BK_dStart D, BK_dEnd D, BK_dPrEnd D)
 
SELECT curRoom
INDEX on DTOS(BR_dStart) DESCENDING TAG Date
 
*
*  if we don’t do this, the form shows up with the last table we opened in the page 1 grdnav,
*    since we don’t have a controlsource set on that grid until the Apply button is clicked.
*
SELECT 0
 
RETURN lok
ENDPROC

frmPilgrims.Init
The class library we use with VFP provides an Init method that fires after the Load method. In Load the various objects for the form have not been instantiated yet and in Init those objects exist and can be manipulated. The class library also provides a ShellAdditionalInit method which fires at the end of Init and is used by the programmer to add additional action to those provided by Init.
     Here we had to subclass Init because we need to add parameters 4 and 5 for the optional PM_ID of one pilgrim and the option to make the form modal. Usually this PM_ID is left blank. See below the DODEFAULT() call. That causes the INIT code of the class library also to be executed in addition to our code here.

PROCEDURE Init
LPARAMETERS tuInitialValue, tcFilter, tcAddOnTheFlyAlias, tnPMID, tnModal
 
*
*  4th parameter is optional. if given we do not want to present page 1 for pilgrim selection.
*    Instead we want to jump to page 2 with the pilgrim pre-selected. This will NOT be easy…
*
IF PCOUNT() > 3 AND TYPE(“tnPMID”)=”N”
   thisform.nPMID = tnPMID
ENDIF
 
IF PCOUNT() >= 5 AND TYPE(“tnModal”)=”N” AND tnModal=1
   *
   *  if called for a pilgrim selected from the facilchart or the roomassignment chart,
   *    we want to honor the request to be modal via new optional 5th param (31May06)
   *
   thisform.WindowType = 1
ENDIF
 
RETURN DODEFAULT(tuInitialValue, tcFilter, tcAddOnTheFlyAlias)
ENDPROC

frmPilgrims.ShellAdditionalInit
This is run by the VFP class library at the succesful return from the INIT method (above here). By this time the objects of the form like Tabs and grids exist and can be adjusted for startup.

PROCEDURE shelladditionalinit
*
*  19jun25 fix a weird problem I can’t explain. Sometimes this window
*    seems to maximize itself. Very annoying. add 19jun25
*
IF this.WindowState != 0
   this.Height = 670
   this.Height = 950
   this.WindowState = 0
ENDIF
 
*
*  experiment with removal of the prompts we don’t need. if not possible because
*    of inheritance, we’ll just make them invisible…
*
***thisform.removeobject(“this.pgfpagerefresh1.page1.lblbase1”)
***thisform.removeobject(“this.pgfpagerefresh1.page1.txtFind”)
STORE .F. TO ;
   thisform.pgfpagerefresh1.page1.lblbase1.Visible, ;
   thisform.pgfpagerefresh1.page1.txtFind.visible, ;
   thisform.pgfpagerefresh1.page1.txtFind.tabstop, ;
   thisform.cmdPrint.Visible,thisform.cmdPrint.tabstop, ;
   thisform.cmdAdd.Visible,thisform.cmdAdd.tabstop, ;
   thisform.cmdDelete.Visible,thisform.cmdDelete.tabstop
 
STORE -100 TO ;
   thisform.cmdPrint.Top, ;
   thisform.cmdAdd.Top, ;
   thisform.cmdDelete.Top
 
*
*  get connect handle
*
thisform.nHandle = oLib.oConnectionSVC.GetSingleConnectHandle()
IF thisform.nHandle <= 0
   =MESSAGEBOX(“Unable to connect to SQL Server”,MBEO,”Communication Problem”)
   RETURN .T.
ENDIF
 
*
*  test for initial setup to a pre-selected pilgrim. NOTE and WARNING–this logic
*    replicates much of the logic in the Apply button Click method on page 1.
*    faults in one place will probably require correction in the other. Naturally,
*    we could turn this code into a separate method…
*
IF thisform.nPMID > 0
   *
   *  make sure grdnav’s record source is set and ready to be appended to
   *
   SELECT curPilgrims
   WITH thisform.pgfpagerefresh1.page1
 
   *
   *  cursor was created in Load method, too early to insert here
   *
   .grdnav.recordsource = “curPilgrims”
   .grdnav.setcolumncontrolsources()
 
   *
   *  ok, time to do the retrieve
   *
   lcCmd = “EXEC USP_Ret_Pilgrims @tPMID=”+LTRIM(STR(thisform.nPMID))
   lnResult = H2SQLexe(thisform.nHandle, lcCmd, “RS_Pilgrims”)
   IF lnResult <= 0
      *
      *  error message will have been reported by h2Sqlexe
      *
      RETURN .T.
   ENDIF
   IF !USED(“RS_Pilgrims”)
      RETURN .T.
   ENDIF
   IF RECCOUNT(“RS_Pilgrims”) < 1
      WAIT WINDOW “No matching Pilgrims found” timeout(1)
      RETURN .T.
   ENDIF
 
   *
   *  install result set rows in our cursor for display
   *
   SELECT RS_Pilgrims
   lcDBF = DBF()
 
   *
   *  defend against null birthdate. Keep this code unless/until birthdt
   *    becomes a required field.
   *
   REPLACE ALL PM_dBirth WITH {//} FOR ISNULL(PM_dBirth)
   *  added support for this newly-added date 16Nov07. often this will be unknown, 
   *    depending on the scope, and we return 1/1/1900 as the date when the date can not
   *    be known. this prevents NULL conversion problems in VFP.
   *
   REPLACE ALL VT_dUltstart WITH {//} FOR TTOD(VT_dUltstart)=DATE(1900,1,1)   &&  added 16Nov07
 
   SELECT curPilgrims
   APPEND FROM &lcDBF   &&  insert rows, rebuild indexes
   GOTO TOP
 
   *
   *  refresh the grid. indexes still be valid
   *
   .grdnav.refresh()
   ENDWITH
 
   *
   *  preselect him/her
   *
****   thisform.pgfpagerefresh1.ActivePage = 3
 
   *
   *  added 04feb16. This code didn’t work in Init, where we first see that the caller (may have)
   *    supplied a specific pre-selected PMID. If so, we find that the calculated things on 
   *    page 2 were not being set–the age and the color of the buttons we use to display
   *    notes about the pilgim. The test for PMID>0 needs to be done here after we have created the
   *    curpilgrims cursor for the selected pilgrim.
   *
   thisform.pilgrimreset()
   
   *
   *  from here all references to RS_Pilgrims need to be edited to curPilgrims instead
   *
   USE IN RS_Pilgrims
ENDIF
 
WITH thisform.pgfpagerefresh1.page1.grdnav
   *
   *  cursor was created in Load method, too early to do this code
   *
   .recordsource = “curPilgrims”
   .setcolumncontrolsources()
ENDWITH
 
*
*  create 4 parameter blocks in support of the carry foward feature. 
*    the savaction() method passes these to the containers’
*    SAVE(pilgrims,visits,bookings,room) methods. 
*
*  creates a 5th parameter block for frmUT22groups usage
*
IF ATC(“H2CUSTOM”,SET(“CLASSLIB”,1)) = 0
   SET CLASSLIB TO h2custom.vcx ADDITIVE
ENDIF
thisform.createparamblocks()
 
*
*  instantiate, but don’t show, the carry forward form. The form is SHOWn by
*    the Add buttons for Pilgrims (on this form), Visits, Bookings, Rooms
*    (the latter 3 are buttons on the containers).
*
PUBLIC goCarryForm
goCarryform = CREATEOBJECT(“frmH2Carry”)
thisform.oCarryform = goCarryform
 
*
*  special logic in support of read-only access to this form by HK-Maint depts.
*    note that the icInitialMPR property is protected…
*
lcString = oMenu.getPProp(“icInitialMPR”)
IF TYPE(“lcString”) = “C” AND !”H2″ $ UPPER(lcString)
   *
   *  have an alternate main menu (not H2Main.MPR). For all alt menus (for now
   *    anyway) our assumption is that one only has read-only access to Pilgrims,
   *    Visits, Bookings and Roomassignments.
   *
   this.ilNodataentry = .T.
ENDIF
 
RETURN .T.
ENDPROC

frmPilgrims.Destroy
This method is fired by the class library when the user closed the form. A chance to clean up.

PROCEDURE Destroy
IF USED(“RS_Pilgrims”)
   USE IN RS_Pilgrims
ENDIF
 
IF USED(“RS_Visits”)
   USE IN RS_Visits
ENDIF
 
IF USED(“RS_Kitchens”)
   USE IN RS_Kitchens
ENDIF
 
IF USED(“RS_Facilities”)
   USE IN RS_Facilities
ENDIF
 
IF USED(“curGroups”)
   lc = UPPER(DBF(“curGroups”))
   lc2 = STRTRAN(lc,”.DBF”,”.CDX”)
   lc3 = STRTRAN(lc,”.DBF”,”.FPT”)
   USE IN curGroups
   ERASE &lc3
   ERASE &lc2
   ERASE &lc
ENDIF
 
IF USED(“curPilgrims”)
   lc = UPPER(DBF(“curPilgrims”))
   lc2 = STRTRAN(lc,”.DBF”,”.CDX”)
   lc3 = STRTRAN(lc,”.DBF”,”.FPT”)
   USE IN curPilgrims
   ERASE &lc3
   ERASE &lc2
   ERASE &lc
ENDIF
 
IF USED(“curBookings”)
   lc = UPPER(DBF(“curBookings”))
   lc2 = STRTRAN(lc,”.DBF”,”.CDX”)
   lc3 = STRTRAN(lc,”.DBF”,”.FPT”)
   USE IN curBookings
   ERASE &lc3
   ERASE &lc2
   ERASE &lc
ENDIF
 
IF USED(“curRoom”)
   lc = UPPER(DBF(“curRoom”))
   lc2 = STRTRAN(lc,”.DBF”,”.CDX”)
   lc3 = STRTRAN(lc,”.DBF”,”.FPT”)
   USE IN curRoom
   ERASE &lc3
   ERASE &lc2
   ERASE &lc
ENDIF
 
IF USED(“curVisits”)
   lc = UPPER(DBF(“curVisits”))
   lc2 = STRTRAN(lc,”.DBF”,”.CDX”)
   lc3 = STRTRAN(lc,”.DBF”,”.FPT”)
   USE IN curVisits
   ERASE &lc3
   ERASE &lc2
   ERASE &lc
ENDIF
 
*
*  release the 5 objects we created for carry forward processing
*
**Public goPM,goVT,goBK,goBR
IF TYPE(“goPM”)==”O”
   goPM = .NULL.
   RELEASE goPM
ENDIF
IF TYPE(“goVT”)==”O”
   goVT = .NULL.
   RELEASE goVT
ENDIF
IF TYPE(“goBK”)==”O”
   goBK = .NULL.
   RELEASE goBK
ENDIF
IF TYPE(“goBR”)==”O”
   goBR = .NULL.
   RELEASE goBR
ENDIF
 
IF TYPE(“thisform.oPMparamblock”)==”O”
   thisform.oPMparamblock = .NULL.
ENDIF
IF TYPE(“thisform.oVTparamblock”)==”O”
   thisform.oVTparamblock = .NULL.
ENDIF
IF TYPE(“thisform.oBKparamblock”)==”O”
   thisform.oBKparamblock = .NULL.
ENDIF
IF TYPE(“thisform.oBRparamblock”)==”O”
   thisform.oBRparamblock = .NULL.
ENDIF
 
IF TYPE(“thisform.oCarryform”)==”O”
   thisform.oCarryform = .NULL.
   goCarryform=.NULL.
   RELEASE goCarryform
ENDIF
 
SET DELETED ON
RETURN DODEFAULT()
ENDPROC

frmPilgrims.SetMode
The form keeps track of what mode it is in. Of the options modes that are supported by our class library only 2 are used in frmPilgrims:
     ‘D’ = default mode, no Save or Cancel are pending
     ‘E’ – Edit mode in which an Add or an Edit is required.
The InteractiveChange method of just about every textbox, combo box,optionbuttongroup etc on the 5 Tabs fires when the op makes a change, and that method calls thisform.SetMode to go into Edit mode, which turns on the Visible property of the Save and Cancel buttons. When a Save or Cancel are completed this method is called to go back into Default mode, making Save and Cancel invisible.

PROCEDURE setmode

LPARAMETERS tcMode
 
*
*  special handling–a control’s onchange() method has detected a change,
*    and the control is bound to a field or property. We need to determine
*    which table was altered for our custom save logic.
*  in the case of hBookings, we also need to determine which ROW was altered
*    and how. So, here’s where we do it, highly dependent on page placement, 
*    meaning that if we re-arrange pages this code will break but probably
*    not crash, which could be bad…
*
*  note that we record the mode into the curBookings row via the curBookings.lMod,
*    lDel and lAdd flags. We’ll need this in our replacement for thisform.Saveaction(),
*    when deciding what action to perform on a row.
*
LOCAL ln, lok
 
*
*  normal behavior first
*
lok = DODEFAULT(tcMode)
 
IF tcMode = “D”
   *
   *  normal behavior is fine is going back to Default mode (means Save or Cancel is complete)
   *
   RETURN lok
ENDIF
 
IF lok
   ln = this.pgfpagerefresh1.ActivePage
 
   DO case
   CASE ln = 2
      thisform.lModpilgrims = .t.
 
   CASE ln = 3
      thisform.lModvisits = .t.
 
   CASE ln = 4
      thisform.lModbookings = .t.
 
   CASE ln = 5
      thisform.lModrooms = .t.
   ENDCASE
ENDIF
 
RETURN lok
ENDPROC

frmPilgrims.createparamblocks
Creates 4 objects during startup that are used to retain pilgrim data that can be carried forward for the data entry of a group of pilgrims. If a family needs reservations and the op right clicks the Add Pilgrim button for each family member, then their address information other typically matching data can be applied without extra data entry to all the members.

*– Creates 4 parameter blocks using the cush2carry class, for use by saveaction()
PROCEDURE createparamblocks
*
*  called from shelladditionalinit().  here we create 4 parameter blocks to be passed
*    to the show() method of a form instantited from the frmh2Carry class.
*
*  15Nov07 added another parameter block we can use to pass to the frmPickGroup.scx
*    Group picklist. if the user makes a Group selection, we receive the GP_ID and
*    the GP_cName fields in the block.
*
Public goPM,goVT,goBK,goBR,goGP
 
goPM = CREATEOBJECT(“cusH2paramblock”)
thisform.oPMparamblock = goPM
DIMENSION goPM.aParameters(13,PB_FIELD_DISPVAL)
 
goPM.aParameters(1,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(1,PB_FIELD_LABEL) = “Last name”
goPM.aParameters(1,PB_FIELD_TYPE) = “C”
goPM.aParameters(1,PB_FIELD_NAME) = “curPilgrims.PM_cLast”
goPM.aParameters(1,PB_FIELD_VALUE) = “”
goPM.aParameters(1,PB_FIELD_DISPNAME) = “”
goPM.aParameters(1,PB_FIELD_DISPVAL) = “”
 
goPM.aParameters(2,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(2,PB_FIELD_LABEL) = “Address 1”
goPM.aParameters(2,PB_FIELD_TYPE) = “C”
goPM.aParameters(2,PB_FIELD_NAME) = “curPilgrims.PM_cAddr1”
goPM.aParameters(2,PB_FIELD_VALUE) = “”
goPM.aParameters(2,PB_FIELD_DISPNAME) = “”
goPM.aParameters(2,PB_FIELD_DISPVAL) = “”
 
goPM.aParameters(3,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(3,PB_FIELD_LABEL) = “Address 2”
goPM.aParameters(3,PB_FIELD_TYPE) = “C”
goPM.aParameters(3,PB_FIELD_NAME) = “curPilgrims.PM_cAddr2”
goPM.aParameters(3,PB_FIELD_VALUE) = “”
goPM.aParameters(3,PB_FIELD_DISPNAME) = “”
goPM.aParameters(3,PB_FIELD_DISPVAL) = “”
 
goPM.aParameters(4,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(4,PB_FIELD_LABEL) = “Address 3”
goPM.aParameters(4,PB_FIELD_TYPE) = “C”
goPM.aParameters(4,PB_FIELD_NAME) = “curPilgrims.PM_cAddr3”
goPM.aParameters(4,PB_FIELD_VALUE) = “”
goPM.aParameters(4,PB_FIELD_DISPNAME) = “”
goPM.aParameters(4,PB_FIELD_DISPVAL) = “”
 
goPM.aParameters(5,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(5,PB_FIELD_LABEL) = “City”
goPM.aParameters(5,PB_FIELD_TYPE) = “C”
goPM.aParameters(5,PB_FIELD_NAME) = “curPilgrims.PM_cCity”
goPM.aParameters(5,PB_FIELD_VALUE) = “”
goPM.aParameters(5,PB_FIELD_DISPNAME) = “”
goPM.aParameters(5,PB_FIELD_DISPVAL) = “”
 
goPM.aParameters(6,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(6,PB_FIELD_LABEL) = “State”
goPM.aParameters(6,PB_FIELD_TYPE) = “C”
goPM.aParameters(6,PB_FIELD_NAME) = “curPilgrims.PM_cState”
goPM.aParameters(6,PB_FIELD_VALUE) = “”
goPM.aParameters(6,PB_FIELD_DISPNAME) = “”
goPM.aParameters(6,PB_FIELD_DISPVAL) = “”
 
goPM.aParameters(7,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(7,PB_FIELD_LABEL) = “ZCode”
goPM.aParameters(7,PB_FIELD_TYPE) = “C”
goPM.aParameters(7,PB_FIELD_NAME) = “curPilgrims.PM_cZCode”
goPM.aParameters(7,PB_FIELD_VALUE) = “”
goPM.aParameters(7,PB_FIELD_DISPNAME) = “”
goPM.aParameters(7,PB_FIELD_DISPVAL) = “”
 
goPM.aParameters(8,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(8,PB_FIELD_LABEL) = “Country”
goPM.aParameters(8,PB_FIELD_TYPE) = “C”
goPM.aParameters(8,PB_FIELD_NAME) = “curPilgrims.PM_cCountry”
goPM.aParameters(8,PB_FIELD_VALUE) = “”
goPM.aParameters(8,PB_FIELD_DISPNAME) = “”
goPM.aParameters(8,PB_FIELD_DISPVAL) = “”
 
goPM.aParameters(9,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(9,PB_FIELD_LABEL) = “Phone”
goPM.aParameters(9,PB_FIELD_TYPE) = “C”
goPM.aParameters(9,PB_FIELD_NAME) = “curPilgrims.PM_cPhone”
goPM.aParameters(9,PB_FIELD_VALUE) = “”
goPM.aParameters(9,PB_FIELD_DISPNAME) = “”
goPM.aParameters(9,PB_FIELD_DISPVAL) = “”
 
goPM.aParameters(10,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(10,PB_FIELD_LABEL) = “Phone 2”
goPM.aParameters(10,PB_FIELD_TYPE) = “C”
goPM.aParameters(10,PB_FIELD_NAME) = “curPilgrims.PM_cPhone2”
goPM.aParameters(10,PB_FIELD_VALUE) = “”
goPM.aParameters(10,PB_FIELD_DISPNAME) = “”
goPM.aParameters(10,PB_FIELD_DISPVAL) = “”
 
goPM.aParameters(11,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(11,PB_FIELD_LABEL) = “Cell #”
goPM.aParameters(11,PB_FIELD_TYPE) = “C”
goPM.aParameters(11,PB_FIELD_NAME) = “curPilgrims.PM_cCell”
goPM.aParameters(11,PB_FIELD_VALUE) = “”
goPM.aParameters(11,PB_FIELD_DISPNAME) = “”
goPM.aParameters(11,PB_FIELD_DISPVAL) = “”
 
goPM.aParameters(12,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(12,PB_FIELD_LABEL) = “Email”
goPM.aParameters(12,PB_FIELD_TYPE) = “C”
goPM.aParameters(12,PB_FIELD_NAME) = “curPilgrims.PM_cEmail”
goPM.aParameters(12,PB_FIELD_VALUE) = “”
goPM.aParameters(12,PB_FIELD_DISPNAME) = “”
goPM.aParameters(12,PB_FIELD_DISPVAL) = “”
 
goPM.aParameters(13,PB_FLAG) = PB_CARRYFWD
goPM.aParameters(13,PB_FIELD_LABEL) = “Cform”
goPM.aParameters(13,PB_FIELD_TYPE) = “L”
goPM.aParameters(13,PB_FIELD_NAME) = “curPilgrims.PM_lCform”
goPM.aParameters(13,PB_FIELD_VALUE) = “”
goPM.aParameters(13,PB_FIELD_DISPNAME) = “”
goPM.aParameters(13,PB_FIELD_DISPVAL) = “”
 
 
goVT = CREATEOBJECT(“cusH2paramblock”)
thisform.oVTparamblock = goVT
DIMENSION goVT.aParameters(10,PB_FIELD_DISPVAL)
 
goVT.aParameters(1,PB_FLAG) = PB_CARRYFWD
goVT.aParameters(1,PB_FIELD_LABEL) = “Ult start date”
goVT.aParameters(1,PB_FIELD_TYPE) = “D”
goVT.aParameters(1,PB_FIELD_NAME) = “curVisits.VT_dUltstart”
goVT.aParameters(1,PB_FIELD_VALUE) = “”
goVT.aParameters(1,PB_FIELD_DISPNAME) = “”
goVT.aParameters(1,PB_FIELD_DISPVAL) = “”
 
goVT.aParameters(2,PB_FLAG) = PB_CARRYFWD
goVT.aParameters(2,PB_FIELD_LABEL) = “Ult depart date”
goVT.aParameters(2,PB_FIELD_TYPE) = “D”
goVT.aParameters(2,PB_FIELD_NAME) = “curVisits.VT_dUltdepart”
goVT.aParameters(2,PB_FIELD_VALUE) = “”
goVT.aParameters(2,PB_FIELD_DISPNAME) = “”
goVT.aParameters(2,PB_FIELD_DISPVAL) = “”
 
goVT.aParameters(3,PB_FLAG) = PB_CARRYFWD
goVT.aParameters(3,PB_FIELD_LABEL) = “Requested facil”
goVT.aParameters(3,PB_FIELD_TYPE) = “C”
goVT.aParameters(3,PB_FIELD_NAME) = “curVisits.FA_cCode”
goVT.aParameters(3,PB_FIELD_VALUE) = “”
goVT.aParameters(3,PB_FIELD_DISPNAME) = “”
goVT.aParameters(3,PB_FIELD_DISPVAL) = “”
 
goVT.aParameters(4,PB_FLAG) = PB_CARRYFWD
goVT.aParameters(4,PB_FIELD_LABEL) = “Req single”
goVT.aParameters(4,PB_FIELD_TYPE) = “L”
goVT.aParameters(4,PB_FIELD_NAME) = “curVisits.VT_lReqsgl”
goVT.aParameters(4,PB_FIELD_VALUE) = “”
goVT.aParameters(4,PB_FIELD_DISPNAME) = “”
goVT.aParameters(4,PB_FIELD_DISPVAL) = “”
 
goVT.aParameters(5,PB_FLAG) = PB_CARRYFWD
goVT.aParameters(5,PB_FIELD_LABEL) = “Req double”
goVT.aParameters(5,PB_FIELD_TYPE) = “L”
goVT.aParameters(5,PB_FIELD_NAME) = “curVisits.VT_lReqdbl”
goVT.aParameters(5,PB_FIELD_VALUE) = “”
goVT.aParameters(5,PB_FIELD_DISPNAME) = “”
goVT.aParameters(5,PB_FIELD_DISPVAL) = “”
 
goVT.aParameters(6,PB_FLAG) = PB_CARRYFWD
goVT.aParameters(6,PB_FIELD_LABEL) = “Snores”
goVT.aParameters(6,PB_FIELD_TYPE) = “L”
goVT.aParameters(6,PB_FIELD_NAME) = “curVisits.VT_lSnores”
goVT.aParameters(6,PB_FIELD_VALUE) = “”
goVT.aParameters(6,PB_FIELD_DISPNAME) = “”
goVT.aParameters(6,PB_FIELD_DISPVAL) = “”
 
goVT.aParameters(7,PB_FLAG) = PB_CARRYFWD
goVT.aParameters(7,PB_FIELD_LABEL) = “Light sleeper”
goVT.aParameters(7,PB_FIELD_TYPE) = “L”
goVT.aParameters(7,PB_FIELD_NAME) = “curVisits.VT_lLsleep”
goVT.aParameters(7,PB_FIELD_VALUE) = “”
goVT.aParameters(7,PB_FIELD_DISPNAME) = “”
goVT.aParameters(7,PB_FIELD_DISPVAL) = “”
 
goVT.aParameters(8,PB_FLAG) = PB_CARRYFWD
goVT.aParameters(8,PB_FIELD_LABEL) = “Arrival time”
goVT.aParameters(8,PB_FIELD_TYPE) = “C”
goVT.aParameters(8,PB_FIELD_NAME) = “curVisits.VT_cArrtime”
goVT.aParameters(8,PB_FIELD_VALUE) = “”
goVT.aParameters(8,PB_FIELD_DISPNAME) = “”
goVT.aParameters(8,PB_FIELD_DISPVAL) = “”
 
goVT.aParameters(9,PB_FLAG) = PB_CARRYFWD
goVT.aParameters(9,PB_FIELD_LABEL) = “Roommate_Notes”
goVT.aParameters(9,PB_FIELD_TYPE) = “C”
goVT.aParameters(9,PB_FIELD_NAME) = “curVisits.VT_mRoomnotes”
goVT.aParameters(9,PB_FIELD_VALUE) = “”
goVT.aParameters(9,PB_FIELD_DISPNAME) = “”
goVT.aParameters(9,PB_FIELD_DISPVAL) = “”
 
goVT.aParameters(10,PB_FLAG) = PB_CARRYFWD
goVT.aParameters(10,PB_FIELD_LABEL) = “Group”
goVT.aParameters(10,PB_FIELD_TYPE) = “N”
goVT.aParameters(10,PB_FIELD_NAME) = “curVisits.VT_GPID”
goVT.aParameters(10,PB_FIELD_VALUE) = “”
goVT.aParameters(10,PB_FIELD_DISPNAME) = “curGroups.GP_cName”
goVT.aParameters(10,PB_FIELD_DISPVAL) = “”
 
 
goBK = CREATEOBJECT(“cusH2paramblock”)
thisform.oBKparamblock = goBK
DIMENSION goBK.aParameters(8,PB_FIELD_DISPVAL)
 
goBK.aParameters(1,PB_FLAG) = PB_CARRYFWD
goBK.aParameters(1,PB_FIELD_LABEL) = “Booking start dt”
goBK.aParameters(1,PB_FIELD_TYPE) = “D”
goBK.aParameters(1,PB_FIELD_NAME) = “curBookings.BK_dStart”
goBK.aParameters(1,PB_FIELD_VALUE) = “”
goBK.aParameters(1,PB_FIELD_DISPNAME) = “”
goBK.aParameters(1,PB_FIELD_DISPVAL) = “”
 
goBK.aParameters(2,PB_FLAG) = PB_CARRYFWD
goBK.aParameters(2,PB_FIELD_LABEL) = “Booking end dt”
goBK.aParameters(2,PB_FIELD_TYPE) = “D”
goBK.aParameters(2,PB_FIELD_NAME) = “curBookings.BK_dEnd”
goBK.aParameters(2,PB_FIELD_VALUE) = “”
goBK.aParameters(2,PB_FIELD_DISPNAME) = “”
goBK.aParameters(2,PB_FIELD_DISPVAL) = “”
 
goBK.aParameters(3,PB_FLAG) = PB_CARRYFWD
goBK.aParameters(3,PB_FIELD_LABEL) = “Facility”
goBK.aParameters(3,PB_FIELD_TYPE) = “C”
goBK.aParameters(3,PB_FIELD_NAME) = “curBookings.FA_cCode”
goBK.aParameters(3,PB_FIELD_VALUE) = “”
goBK.aParameters(3,PB_FIELD_DISPNAME) = “”
goBK.aParameters(3,PB_FIELD_DISPVAL) = “”
 
goBK.aParameters(4,PB_FLAG) = PB_CARRYFWD
goBK.aParameters(4,PB_FIELD_LABEL) = “First meal”
goBK.aParameters(4,PB_FIELD_TYPE) = “N”
goBK.aParameters(4,PB_FIELD_NAME) = “curBookings.BK_nFirstmeal”
goBK.aParameters(4,PB_FIELD_VALUE) = “”
goBK.aParameters(4,PB_FIELD_DISPNAME) = “”
goBK.aParameters(4,PB_FIELD_DISPVAL) = “”
 
goBK.aParameters(5,PB_FLAG) = PB_CARRYFWD
goBK.aParameters(5,PB_FIELD_LABEL) = “Last meal”
goBK.aParameters(5,PB_FIELD_TYPE) = “N”
goBK.aParameters(5,PB_FIELD_NAME) = “curBookings.BK_nLastmeal”
goBK.aParameters(5,PB_FIELD_VALUE) = “”
goBK.aParameters(5,PB_FIELD_DISPNAME) = “”
goBK.aParameters(5,PB_FIELD_DISPVAL) = “”
 
goBK.aParameters(6,PB_FLAG) = PB_CARRYFWD
goBK.aParameters(6,PB_FIELD_LABEL) = “With opp gender?”
goBK.aParameters(6,PB_FIELD_TYPE) = “L”
goBK.aParameters(6,PB_FIELD_NAME) = “curBookings.BK_lOppgend”
goBK.aParameters(6,PB_FIELD_VALUE) = “”
goBK.aParameters(6,PB_FIELD_DISPNAME) = “”
goBK.aParameters(6,PB_FIELD_DISPVAL) = “”
 
goBK.aParameters(7,PB_FLAG) = PB_CARRYFWD
goBK.aParameters(7,PB_FIELD_LABEL) = “Arrival time”
goBK.aParameters(7,PB_FIELD_TYPE) = “C”
goBK.aParameters(7,PB_FIELD_NAME) = “curBookings.BK_cArrtime”
goBK.aParameters(7,PB_FIELD_VALUE) = “”
goBK.aParameters(7,PB_FIELD_DISPNAME) = “”
goBK.aParameters(7,PB_FIELD_DISPVAL) = “”
 
goBK.aParameters(8,PB_FLAG) = PB_CARRYFWD
goBK.aParameters(8,PB_FIELD_LABEL) = “Booking_Notes”
goBK.aParameters(8,PB_FIELD_TYPE) = “C”
goBK.aParameters(8,PB_FIELD_NAME) = “curBookings.BK_mNotes”
goBK.aParameters(8,PB_FIELD_VALUE) = “”
goBK.aParameters(8,PB_FIELD_DISPNAME) = “”
goBK.aParameters(8,PB_FIELD_DISPVAL) = “”
 
 
goBR = CREATEOBJECT(“cusH2paramblock”)
thisform.oBRparamblock = goBR
DIMENSION goBR.aParameters(4,PB_FIELD_DISPVAL)
 
goBR.aParameters(1,PB_FLAG) = PB_CARRYFWD
goBR.aParameters(1,PB_FIELD_LABEL) = “Start date”
goBR.aParameters(1,PB_FIELD_TYPE) = “D”
goBR.aParameters(1,PB_FIELD_NAME) = “curRoom.BR_dStart”
goBR.aParameters(1,PB_FIELD_VALUE) = “”
goBR.aParameters(1,PB_FIELD_DISPNAME) = “”
goBR.aParameters(1,PB_FIELD_DISPVAL) = “”
 
goBR.aParameters(2,PB_FLAG) = PB_CARRYFWD
goBR.aParameters(2,PB_FIELD_LABEL) = “End date”
goBR.aParameters(2,PB_FIELD_TYPE) = “D”
goBR.aParameters(2,PB_FIELD_NAME) = “curRoom.BR_dEnd”
goBR.aParameters(2,PB_FIELD_VALUE) = “”
goBR.aParameters(2,PB_FIELD_DISPNAME) = “”
goBR.aParameters(2,PB_FIELD_DISPVAL) = “”
 
goBR.aParameters(3,PB_FLAG) = PB_CARRYFWD
goBR.aParameters(3,PB_FIELD_LABEL) = “Facility”
goBR.aParameters(3,PB_FIELD_TYPE) = “C”
goBR.aParameters(3,PB_FIELD_NAME) = “curRoom.FA_cCode”
goBR.aParameters(3,PB_FIELD_VALUE) = “”
goBR.aParameters(3,PB_FIELD_DISPNAME) = “”
goBR.aParameters(3,PB_FIELD_DISPVAL) = “”
 
goBR.aParameters(4,PB_FLAG) = PB_CARRYFWD
goBR.aParameters(4,PB_FIELD_LABEL) = “Room code”
goBR.aParameters(4,PB_FIELD_TYPE) = “C”
goBR.aParameters(4,PB_FIELD_NAME) = “curRoom.RM_cCode”
goBR.aParameters(4,PB_FIELD_VALUE) = “”
goBR.aParameters(4,PB_FIELD_DISPNAME) = “”
goBR.aParameters(4,PB_FIELD_DISPVAL) = “”
 
 
* —————————————————
*
*  new parameter block for frmUT22Groups
*
* —————————————————
goGP = CREATEOBJECT(“cusH2paramblock”)
thisform.oGPparamblock = goGP
***
***  modified this 15jan10 to add a 3rd row for flag indicating whether
***    group status of the pilgrim(s) have been modified by the called program
***
DIMENSION goGP.aParameters(3,2)
 
goGP.aParameters(1,1) = “GP_ID”
goGP.aParameters(1,2) = 0
goGP.aParameters(2,1) = “GP_cName”
goGP.aParameters(2,2) = “”
goGP.aParameters(3,1) = “ModFlag”
goGP.aParameters(3,2) = .F.
 
RETURN .t.
ENDPROC

frmPilgrims.FixQuote
This method is also in the Save group of methods. Simply tirns one single quote in a text field into a pair of single quotes, for SQL syntax.

*– pairs any single quotes in a parameter, so that SQL Server will not bomb when that string is put into a parameter list for a SP
PROCEDURE fixquote
LPARAMETERS tcString
 
IF EMPTY(tcString) OR NOT “‘” $ tcString
   RETURN tcString
ENDIF
 
ln = LEN(tcString)
DO WHILE ln > 0
   *
   *  find right-most unrepaired quote. we work from the back because
   *    the string grows for every unpaired quote we double, so the math
   *    is much easier working from the right.
   *
   lnQ = RAT(“‘”,LEFT(tcString,ln))
   
   DO CASE
   CASE lnQ=0
      *
      *  none left to fix
      *
      EXIT
   CASE lnQ = 1
      *
      *  fix leading quote in col 1
      *
      tcString = “‘”+tcString
      EXIT
   ENDCASE
   
   *
   *  see if this quote is paired
   *
   lnQ = lnQ-1
   IF SUBSTR(tcString,lnQ,1) <> “‘”
      *
      *  add a quote
      *
      tcString = STUFF(tcString,lnQ+2,0,”‘”)
   ENDIF
   
   *
   *  now search only the previous part of the string
   *
   ln = lnQ-1
ENDDO
 
RETURN tcString
ENDPROC

frmPilgrims.UpdateFormOnEdit
Called from the class library. The stock behaviour of UpdateForOnEdit run because of the first line of code “RETURN DODEFAULT()” and the rest is left in for background information. You can just ignore it!

PROCEDURE updateformonedit
RETURN DODEFAULT()
 
 
*
*  This is code copied from:
*
*  XXFWFRM.VCX/frmDEGridNav::UpdateFormOnEdit()
*  XXFW.VCX/frmData::UpdateFormOnEdit()
*
*  We can’t let the normal case code run because of the odd situation we’re
*    in with respect to GETMODE()/SETMODE(), in which when we want to ADD a 
*    new pilgrim record, we don’t really add anything, we just empty the set
*    of properties that hold pilgrim info. Since we need to force Save/Cancel
*    buttons to appear, we go into edit mode. But the code we call from 
*    updateformonedit(),
*      THIS.ioDEPageFrame.OnUpdateFormOnEdit()
*    disables all the pages but page 1, since it notices that the main alias
*    has no records in it, which would have been ok if we had been able to go
*    into add mode rather than edit mode…
*
 
*
*  update the tooltiptext for the txtFind textbox
*
THIS.ioPicklistGrid.PARENT.txtFind.ToolTipText = ; 
     X3I(“Save/Cancel before searching”)
 
THIS.ioPicklistGrid.ToolTipText = X3I(“Save/Cancel before moving”)
 
*
*  Called from THIS.EditAction()
*
*  A simple Refresh() of command buttons to set Enabled/Visible is 
*  included here, but you’re likely to want more specific code, and 
*  we intend that you subclass/override this code
*
LOCAL llLockScreenHere
llLockScreenHere = THIS.LockScreenHere()
THIS.RefreshFormControlsOfBaseClass(“COMMANDBUTTON”,.t.)
 
THIS.UpdateFormCaption()
 
IF VARTYPE(THIS.ioDEPageFrame) = “O”
****  Here is our override, that we comment out this call:
****  THIS.ioDEPageFrame.OnUpdateFormOnEdit()
ENDIF
 
*
*  if there are any participating members
*  inheriting from XXFWGRD.VCX/grdDataEntry,
*  let them know it’s time to participate
*
LOCAL xx
FOR xx = 1 TO ALEN(THIS.iaDataEntryGrids,1)
  IF VARTYPE(THIS.iaDataEntryGrids[xx,1]) = “O”
    THIS.iaDataEntryGrids[xx,1].OnUpdateFormOnEdit()
  ENDIF
ENDFOR  
 
*
*  if there are any participating members
*  inheriting from XXFWFRM.VCX/ctrDERegisteredComposite,
*  let them know it’s time to participate
*
FOR xx = 1 TO ALEN(THIS.iaDERegisteredComposites,1)
  IF VARTYPE(THIS.iaDERegisteredComposites[xx,1]) = “O”
    THIS.iaDERegisteredComposites[xx,1].OnUpdateFormOnEdit()
  ENDIF
ENDFOR  
 
THIS.LockScreenHere(llLockScreenHere) 
 
RETURN .t.
ENDPROC

frmPilgrims.ActionButtonRefreshLogic
You can just ignore this since it just amounts to managing or “faking out” the class library expectations. You would not reproduce this in your rewrite.

PROCEDURE actionbuttonrefreshlogic
LPARAMETERS tcType
 
*
*  force “A”dd, “D”elete and p”R”int to remain invisible
*
IF tcType $ “ADR”
   RETURN “I”
ENDIF
 
RETURN DODEFAULT(tcType)
ENDPROC

FORM frmPilgrims - Tab 3 (Visits) VFP Method Code

Introduction
This code manages…

…Page3.cntDEvisits.FindVisits
Called when a new pilgrim is selected. Since the option button group control on Tab 3 defaults to Current Season, then unless op has changed it this method will populate the curVisits result set with any Visits from the current season, which as of 2026 starts on 1 may 2026.

PROCEDURE findvisits
LPARAMETERS tnID
 
* —————————————————————————-
*  WHEN THIS CONTAINER IS USED IN frmPilgrims:
*
*  We have a newly-selected Pilgrims rcd and the caller wants to go to page 3.
*    Commonpageactivate() has determined that we have not yet looked up the 
*    Visits records, if any, for that Pilgrims rcd. We do that here.
*
*  We call this method from several other methods in this container, e.g. savevisits.
*
*  Another way we get here is that the user has clicked the Scope option button(s)
*    of this container. We are changing scope, so we must re-do the SQL select from hVisits.
*
* —————————————————————————-
*
*  added the optional parameter tnID on 09Oct06 because I want to call this method
*    after a save of a new visit, so that the grid will pop the new visit into the
*    correct sequence, usually the top of a list of visits, whereas it will have started
*    life at the bottom of the list.
LOCAL lnRec, lcRecordSource, lnResult, lcCmd, lcDBF, ln, llok
 
IF PCOUNT() < 1
    tnID = 0         &&  id of currently-highlighted row in curVisits. not required.
ENDIF
 
*
*  get connection handle. Actually H2SQLexe would do that for us…
*
lnHandle = oLib.oConnectionSVC.GetSingleConnectHandle()
IF lnHandle <= 0
   =MESSAGEBOX(“Unable to establish contact with the SQL Server computer”,MBEO,”Connectivity Problem”)
   RETURN 0
ENDIF
 
*
*  pick up the name our navgrd uses
*
lcRecordsource = “curVisits”
 
*
*  empty out the recordsource of the grid, which is a cursor the user must create in
*    the form load method, or the caller of the form this container is on must pass 
*    its name and use the default datasession. Any road, it has to exist and remain
*    fixed (no swapping in recordsources on the fly). This is how we work around the 
*    many grid problems we have seen during development…
*
ln = SELECT()
SELECT (lcRecordsource)         &&  our updatable cursor
this.clearcursor()
 
*
*  create and execute the lookup command
*    19jun25 changed to Ret22 version to avoid ref to VT_lExtLastDay, a field we removed in 2025
*
lcCmd = “EXEC dbo.USP_Ret22_Visits”+ ;
   ” @tPMID = “+LTRIM(STR(thisform.nPMID))+ ;
   “, @tScope=”+LTRIM(STR(this.opgScope.Value))
lnResult = H2SQLexe(lnHandle, lcCmd, “RS_Visits”)
IF lnResult <= 0 or !USED(“RS_Visits”) OR (RECCOUNT(“RS_Visits”) < 1)
   this.newvisits(.T.)      &&  ZAPs curVisits
   SELECT (ln)
   RETURN 0                 &&  indicate 0 Visits rows found
ENDIF
 
*
*  now we have a result set alias to insert into the grid to display some pilgrim(s)
*     clean up the NULL dates for better display
*  (Note–deactivated first and last meal prompts 5June06 at Irene’s request.)
*
*  17jun25 dropped VT_lExtlastday because we now use (correct) new field BK_lExtlastday instead
*
SELECT RS_Visits
lcDBF = DBF()
UPDATE RS_Visits SET VT_dPendarr={//}   WHERE ISNULL(VT_dPendarr)
UPDATE RS_Visits SET VT_dApproxarr={//} WHERE ISNULL(VT_dApproxarr)
UPDATE RS_Visits SET VT_dApproxdep={//} WHERE ISNULL(VT_dApproxdep)
UPDATE RS_Visits SET VT_dUltstart={//}  WHERE ISNULL(VT_dUltstart)
UPDATE RS_Visits SET VT_dUltdepart={//} WHERE ISNULL(VT_dUltdepart)
UPDATE RS_Visits SET VT_cArrtime=’ ‘    WHERE ISNULL(VT_cArrtime)
UPDATE RS_Visits SET FA_cCode=’ ‘       WHERE ISNULL(FA_cCode)
UPDATE RS_Visits SET VT_lPending=0      WHERE ISNULL(VT_lPending)
UPDATE RS_Visits SET VT_lArrived=0      WHERE ISNULL(VT_lArrived)
UPDATE RS_Visits SET VT_lRegist=0       WHERE ISNULL(VT_lRegist)
UPDATE RS_Visits SET VT_lComplete=0     WHERE ISNULL(VT_lComplete)
UPDATE RS_Visits SET VT_lHidden=0       WHERE ISNULL(VT_lHidden)
UPDATE RS_Visits SET VT_lNobill=0       WHERE ISNULL(VT_lNobill)
***UPDATE RS_Visits SET VT_nFirstmeal=0    WHERE ISNULL(VT_nFirstmeal)
***UPDATE RS_Visits SET VT_nLastmeal=0     WHERE ISNULL(VT_nLastmeal)
UPDATE RS_Visits SET VT_nBbookpg=0      WHERE ISNULL(VT_nBbookpg)
UPDATE RS_Visits SET VT_lReqsgl=0       WHERE ISNULL(VT_lReqsgl)
UPDATE RS_Visits SET VT_lReqdbl=0       WHERE ISNULL(VT_lReqdbl)
UPDATE RS_Visits SET VT_lSnores=0       WHERE ISNULL(VT_lSnores)
UPDATE RS_Visits SET VT_lLsleep=0       WHERE ISNULL(VT_lLsleep)
UPDATE RS_Visits SET VT_lLongTermVisa=0 WHERE ISNULL(VT_lLongTermVisa)
UPDATE RS_Visits SET VT_GPID=0          WHERE ISNULL(VT_GPID)
*
*  Here we compensate for the first and last meal so it displays properly
*    in the combo box. Then on a Save, we un-compensate, as follows:
*
*      “@VT_nFirstmeal=”+LTRIM(STR(MAX(CurVisits.VT_nFirstmeal-1,0)))+”, “+ ;
*      “@VT_nLastmeal=”+LTRIM(STR(MAX(CurVisits.VT_nLastmeal-1,0)))+”, ” (etc)
*
***UPDATE RS_Visits SET VT_nFirstmeal = VT_nFirstmeal+1, VT_nLastmeal = VT_nLastmeal+1
 
SELECT (lcRecordsource)  &&  our updatable cursor
APPEND FROM (lcDBF)      &&  our SQLServer-provided result set
GOTO TOP
lnCount = _TALLY
USE IN RS_Visits
 
*
*  new code 16July07 to maintain a copy of the orig UltDepart. We use this in
*    ctrDEBookings.AutocorrectBookings() to see whether the orig departure has
*    been altered and the booking projected ending HAD matched it and should
*    still do so (!).
*  18Nov07 added support for new field VT_dOrigUltstart
*
REPLACE VT_dOrigUltDepart WITH VT_dUltdepart, VT_dOrigUltstart WITH VT_dUltstart ;
   IN (lcRecordsource)
 
*
*  try to highlight the most likely “current” record
*
DO CASE
CASE tnID <> 0
   LOCATE FOR VT_ID = tnID
   IF EOF()
      GOTO TOP
   ENDIF
CASE lnCount > 1
   GOTO TOP
ENDCASE
 
*
*
*
this.newvisits()         &&  (sets thisform.nVTID to the current row in curVisits)
SELECT (ln)
 
RETURN lnCount
ENDPROC

…Page3.cntDEvisits.NewVisits
This method prepares for a new pilgrim, when one has been selected OR when one clicks Clear on Tab 1.

PROCEDURE newvisits
LPARAMETERS tlReset
 
* —————————————————————————
*
*  NewVisits() method. Called from thisform.pageactivate() method.
*    Also called from thisform.pgfpageframe1.ctrSelPilgrims1.cmdClear.Click()
*    …and from this.cmdAddvisit.Click and .Rightclick().
*
* —————————————————————————
LOCAL tlreset, ln, lcrs
 
IF PARAMETERS() < 1
   tlReset = .F.
ENDIF
 
*
*  test for option to clear out properties. we may never need this FOR PILGRIMS,
*    but we certainly will need it for Visits and Bookings when the user clicks the
*    Clear button on page 1.
*
IF tlReset
   *
   *  formerly we set properties, but now the curVisits fields are bound to the 
   *   container’s controls. so instead of blanking properties, it seems we need
   *    to “blank” curVisits instead…after this, caller will issue
   *    INSERT INTO curVisits
   *
   this.clearcursor()
   
   *
   *  added this code 3June06 to be consistent with logic of other containers
   *
   thisform.nPage3PMID = curPilgrims.PM_ID         &&  keep the form current with user’s choice
   thisform.nVTID = 0             &&  indicate no choice has been made yet
   thisform.nBKID = 0
   thisform.nGPID = 0
 
   this.Refresh()
   this.Setcaption()
   RETURN .T.
ENDIF
 
*
*  have a selected Visits record to show on Page 3.
*
SELECT curVisits
thisform.nPage3PMID = curPilgrims.PM_ID         &&  keep the form current with user’s choice
 
*
*  indicate so far no date(s) edited
*
this.lDtChgStart = .F.
this.lDtChgDepart = .F.
 
*
*  new logic 13mar07. if in fact thisform.nVTID is not out of sync, we want to
*    leave thisform.nBKID alone (I think) because we did not change the visit so
*    so why assume bookings are out of date…
*
IF thisform.nVTID != curVisits.VT_ID      &&  added this test 13mar07
   thisform.nVTID = curVisits.VT_ID       &&  keep the form current with user’s choice
   thisform.nGPID = curVisits.VT_GPID     &&  added 15jan10 to retain initial group id (saveaftersuccess())
   thisform.nBKID = 0       &&  if visit changed, children and grandchildren are out of sync
   thisform.nPage4PMID = 0
   thisform.nPage4VTID = 0
   thisform.nPage5PMID = 0
   thisform.nPage5VTID = 0
   thisform.nPage5BKID = 0
ENDIF
 
*
*    Make the new (20Mar09) long term visa flag stand out if it is set
*
IF curVisits.VT_lLongTermVisa = 1
   STORE RGB(255,215,215) TO this.chkLongTermVisa.Backcolor
ELSE
   STORE this.backcolor TO this.chkLongTermVisa.Backcolor
ENDIF
 
*
*    Display the (new) pilgrim name in the lblTitle control
*
this.setcaption()
this.Refresh()
 
RETURN .T.
ENDPROC

…Page3.cntDEvisits.SaveVisits
Called by Saveaction to do Insert or Update to hVisits during a global Save

PROCEDURE savevisits
LPARAMETERS toBlock     &&  added 4June06 to support carry forward feature
*
* the form’s saveaction method first saves any Pilgrim info that has changed,
*    then any Visits, and finally any Bookings. That order is determined by
*    the possibilty that we added a Pilgrims row, in which case we need the ID
*    to put into hVisits, and/ or we added a Visits row in which case we need
*    the new Visits ID here.
*
LOCAL lnVTID
Local lcName,lcDatetime,lcTemp
PRIVATE nIDvalue
 
*
*  remember the ID so we can TRY to restore the rcd pointer (is this row being deleted, added?)
*
lnVTID = curVisits.VT_ID
 
*
*  first defend against visit error.
*
IF !this.checkvisits()
   this.cancelVisits(lnVTID)
   RETURN .F.
ENDIF
 
 
*
*  get the handle first because we know a row needs saving (because we were called by form’s
*    saveaction method based on testing thisform.lModvisits)
*
lnHandle = oLib.oConnectionSVC.GetSingleConnectHandle()
IF lnHandle <= 0
   =MESSAGEBOX(“Unable to establish contact with the SQL Server computer”,MBEO,”Connectivity Problem”)
   RETURN .F.
ENDIF
 
*
*  defend ourselves against a SQL Server crash caused by single quote, unpaired, in any character
*    property
*
REPLACE ;
   VT_cArrtime   WITH thisform.fixquote(CurVisits.VT_cArrtime), ;
   VT_mNotes     WITH thisform.fixquote(CurVisits.VT_mNotes), ;
   VT_mMednotes  WITH thisform.fixquote(CurVisits.VT_mMednotes), ;
   VT_mPendnotes WITH thisform.fixquote(CurVisits.VT_mPendnotes), ;
   VT_mRoomnotes WITH thisform.fixquote(CurVisits.VT_mRoomnotes), ;
   VT_mReqnotes  WITH thisform.fixquote(CurVisits.VT_mReqnotes) IN curVisits
 
*
*  13dec18 new mods to include userid and date in the parameters. there are
*    corresponding mods in the stored procedures and triggers for hPilgrims.
*
lcName = IIF(TYPE(“oApp.cUsername”) = “C”, ;
   oApp.cUsername, “Unknown/Unknown”)
 
*
*  25dec18 modified to include time as well as date in SQL server calls
*
SET CENTURY ON
lcTemp = DTOC(DATETIME())
lcDatetime = SUBSTR(lcTemp,7,4)+’-‘+SUBSTR(lcTemp,4,2)+’-‘+LEFT(lcTemp,2)+’ ‘+TIME()
 
*
*  are we doing an add or an edit? pcMode won’t help. we look at the id number. If an add
*    we assigned a temporary key value of -1.
*
DO CASE
CASE thisform.nVTID < 0
   *
   *  Doing an ADD – prep parameter list for the Insert SP
   *
   nIDvalue = 0
   
***WAIT WINDOW “Visits Insert, User ID = “+lcName+”, Date = “+lcDate timeout(1.8)
   
   *
   *  note in the following stmt that we take VT_FAIDreq from curVisits, NOT from the property
   *  (Note–deactivated first and last meal prompts 5June06 at Irene’s request.)
   *
   *  15Nov07 added support for new column VT_GPID, the hGroups foreign key
   *
   *  06jan19 added the now customary ‘*’ prefix to the BR_cIDupdate, explained in
   *    the trigger
   *
   lcExpression = “?@nIDvalue, “+ ;
      “@VT_PMID=”+LTRIM(STR(thisform.nPMID))+”, “+ ;
      “@VT_FAIDreq=”+LTRIM(STR(curVisits.VT_FAIDreq))+”, “+ ;
      “@VT_dUltstart=”+IIF(!EMPTY(CurVisits.VT_dUltstart),”‘”+STUFF(STUFF(DTOS(CurVisits.VT_dUltstart),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
      “@VT_dUltdepart=”+IIF(!EMPTY(CurVisits.VT_dUltdepart),”‘”+STUFF(STUFF(DTOS(CurVisits.VT_dUltdepart),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
      “@VT_dPendarr=”+IIF(!EMPTY(CurVisits.VT_dPendarr),”‘”+STUFF(STUFF(DTOS(CurVisits.VT_dPendarr),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
      “@VT_dApproxarr=”+IIF(!EMPTY(CurVisits.VT_dApproxarr),”‘”+STUFF(STUFF(DTOS(CurVisits.VT_dApproxarr),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
      “@VT_dApproxdep=”+IIF(!EMPTY(CurVisits.VT_dApproxdep),”‘”+STUFF(STUFF(DTOS(CurVisits.VT_dApproxdep),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
      “@VT_lArrived=”+LTRIM(STR(CurVisits.VT_lArrived))+”, “+ ;
      “@VT_lRegist=”+LTRIM(STR(CurVisits.VT_lRegist))+”, “+ ;
      “@VT_lPending=”+LTRIM(STR(CurVisits.VT_lPending))+”, “+ ;
      “@VT_lComplete=”+LTRIM(STR(CurVisits.VT_lComplete))+”, “+ ;
      “@VT_lHidden=”+LTRIM(STR(CurVisits.VT_lHidden))+”, “+ ;
      “@VT_lNobill=”+IIF(curVisits.VT_lNobill=1,”1″,”0″)+”, “+ ;
      “@VT_nBbookpg=”+LTRIM(STR(CurVisits.VT_nBbookpg))+”, “+ ;
      “@VT_lReqsgl=”+LTRIM(STR(CurVisits.VT_lReqsgl))+”, “+ ;
      “@VT_lReqdbl=”+LTRIM(STR(CurVisits.VT_lReqdbl))+”, “+ ;
      “@VT_lSnores=”+LTRIM(STR(CurVisits.VT_lSnores))+”, “+ ;
      “@VT_lLsleep=”+LTRIM(STR(CurVisits.VT_lLsleep))+”, “+ ;
      “@VT_lLongTermVisa=”+LTRIM(STR(CurVisits.VT_lLongTermVisa))+”, “+ ;
      “@VT_cArrtime='”+TRIM(LEFT(CurVisits.VT_cArrtime,20))+”‘, “+ ;
      “@VT_mNotes='”+TRIM(LEFT(CurVisits.VT_mNotes,250))+”‘, “+ ;
      “@VT_mPendnotes='”+TRIM(LEFT(CurVisits.VT_mPendnotes,250))+”‘, “+ ;
      “@VT_mRoomnotes='”+TRIM(LEFT(CurVisits.VT_mRoomnotes,250))+”‘, “+ ;
      “@VT_mReqnotes='”+TRIM(LEFT(CurVisits.VT_mReqnotes,250))+”‘, “+ ;
      “@VT_GPID=”+LTRIM(STR(CurVisits.VT_GPID))+”, “+ ;
      “@VT_cIDCreate='”+lcName+”‘, “+ ;
      “@VT_dCreate='”+lcDatetime+”‘, “+ ;
      “@VT_cIDupdate=’*”+lcName+”‘, “+ ;
      “@VT_dUpdate='”+lcDatetime+”‘”
 
***SET STEP ON 
***_cliptext = lcExpression
 
*      “@VT_nFirstmeal=”+LTRIM(STR(MAX(CurVisits.VT_nFirstmeal-1,0)))+”, “+ 
*      “@VT_nLastmeal=”+LTRIM(STR(MAX(CurVisits.VT_nLastmeal-1,0)))+”, “+ 
 
   *
   *  insert the row
   *
   lcCmd = “EXEC dbo.USP_Visits_Insert “+lcExpression
   lnResult = H2SQLexe(lnHandle, lcCmd)
 
   IF m.nIDvalue > 0 AND lnResult > 0
      *
      *  insert worked, so retain the real SQL Server-assigned primary key to both 
      *    copies of the VTID. 
      *
      thisform.nVTID = m.nIDvalue
      IF lnVTID < 0
         lnVTID = m.nIDvalue    &&  prep to restore the rcd pointer to this newly-renumbered row
      ENDIF
 
*      *
*      *  now add this new Visits row to the grid. 2 ways we could do this. either by
*      *    INSERT INTO curVisits, or by calling the find method (which we’ll try first).
*      *    (Since Findvisits() recreates the curVisits cursor, it retrieves the rcd we
*      *    just added and links in the related info, like PM_cGender, that we’ll need.)
*      *  31May06…I think this automatically handles the ID issue. We re-fetch the
*      *    visit(s) into curVisits and thus reset the VT_lAdd flag and get the updated ID…
*      *
*      this.findvisits()
 
      *
      *  I don’t like the above code. Here’s why: if you are doing a save of all 4 tiers
      *    and this one happens to be for a new visit (that’s why we’re here) which now
      *    has a new ID, if we go to findvisits() there’s no guaranty as currently designed
      *    that this new visit is the latest one, so that, when findvisits or its lackey
      *    newvisits() does a goto top, we’ll still be on the same curVisits record
      *    that we just added. So for simplicity we do this instead:
      *
      replace VT_ID WITH m.nIDvalue IN curVisits
      
      *
      *  now the last tricky issue (for now–3june06) is what to do with copies of VT_ID
      *    that we use for comparisons. we want them all to match.
      *
      thisform.nVTID = m.nIDvalue     &&  form’s copy, bu what about local copies?
   
      *
      *  now retain the latest field values in the carry forward parameter block
      *
      this.savecarry(toBlock)
 
   ELSE
      *
      *  error handling finale. h2sqlexe already reported the problem to the user.
      *    often it’ll be a usage error, like overlapping dates between 2 visits.
      *
      this.cancelVisits(lnVTID)
      RETURN .F.
   ENDIF
 
CASE thisform.nVTID > 0
   *
   *  Doing an ADD operation
   *
   *  prep the Update param list. Note that the SP logic (at least as first conceived)
   *    requires that we send all fields back, whether they were modified or not.
   *    The Update trigger will determine which fields (if any) were changed,
   *    spin off a hChanges row, and update the timestamp and ID of the Visits row.
   *
   *  (Note–deactivated first and last meal prompts 5June06 at Irene’s request.)
   *
   *  06jan19 added the now customary ‘*’ prefix to the BR_cIDupdate, explained in
   *    the trigger
   *
      PRIVATE nResult
      nResult = 0
 
      *  note in the following stmt that we take VT_FAIDreq from curVisits, NOT from the property
      lcExpression = “?@nResult, “+ ;
      “@VT_ID=”+LTRIM(STR(thisform.nVTID))+”, “+ ;
      “@VT_PMID=”+LTRIM(STR(CurVisits.VT_PMID))+”, “+ ;
      “@VT_FAIDreq=”+LTRIM(STR(curVisits.VT_FAIDreq))+”, “+ ;
      “@VT_dUltstart=”+IIF(!EMPTY(CurVisits.VT_dUltstart),”‘”+STUFF(STUFF(DTOS(CurVisits.VT_dUltstart),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
      “@VT_dUltdepart=”+IIF(!EMPTY(CurVisits.VT_dUltdepart),”‘”+STUFF(STUFF(DTOS(CurVisits.VT_dUltdepart),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
      “@VT_dPendarr=”+IIF(!EMPTY(CurVisits.VT_dPendarr),”‘”+STUFF(STUFF(DTOS(CurVisits.VT_dPendarr),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
      “@VT_dApproxarr=”+IIF(!EMPTY(CurVisits.VT_dApproxarr),”‘”+STUFF(STUFF(DTOS(CurVisits.VT_dApproxarr),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
      “@VT_dApproxdep=”+IIF(!EMPTY(CurVisits.VT_dApproxdep),”‘”+STUFF(STUFF(DTOS(CurVisits.VT_dApproxdep),7,0,”.”),5,0,”.”)+”‘, “,”NULL, “)+ ;
      “@VT_lPending=”+LTRIM(STR(CurVisits.VT_lPending))+”, “+ ;
      “@VT_lArrived=”+LTRIM(STR(CurVisits.VT_lArrived))+”, “+ ;
      “@VT_lRegist=”+LTRIM(STR(CurVisits.VT_lRegist))+”, “+ ;
      “@VT_lComplete=”+LTRIM(STR(CurVisits.VT_lComplete))+”, “+ ;
      “@VT_lHidden=”+LTRIM(STR(CurVisits.VT_lHidden))+”, “+ ;
      “@VT_lNobill=”+IIF(curVisits.VT_lNobill=1,”1″,”0″)+”, “+ ;
      “@VT_nBbookpg=”+LTRIM(STR(CurVisits.VT_nBbookpg))+”, “+ ;
      “@VT_lReqsgl=”+LTRIM(STR(CurVisits.VT_lReqsgl))+”, “+ ;
      “@VT_lReqdbl=”+LTRIM(STR(CurVisits.VT_lReqdbl))+”, “+ ;
      “@VT_lSnores=”+LTRIM(STR(CurVisits.VT_lSnores))+”, “+ ;
      “@VT_lLsleep=”+LTRIM(STR(CurVisits.VT_lLsleep))+”, “+ ;
      “@VT_lLongTermVisa=”+LTRIM(STR(CurVisits.VT_lLongTermVisa))+”, “+ ;
      “@VT_cArrtime='”+TRIM(LEFT(CurVisits.VT_cArrtime,20))+”‘, “+ ;
      “@VT_mNotes='”+TRIM(LEFT(CurVisits.VT_mNotes,250))+”‘, “+ ;
      “@VT_mPendnotes='”+TRIM(LEFT(CurVisits.VT_mPendnotes,250))+”‘, “+ ;
      “@VT_mRoomnotes='”+TRIM(LEFT(CurVisits.VT_mRoomnotes,250))+”‘, “+ ;
      “@VT_mReqnotes='”+TRIM(LEFT(CurVisits.VT_mReqnotes,250))+”‘, “+ ;
      “@VT_GPID=”+LTRIM(STR(CurVisits.VT_GPID))+”, “+ ;
      “@VT_cIDupdate=’*”+lcName+”‘, “+ ;
      “@VT_dUpdate='”+lcDatetime+”‘ “
      
      *** “@VT_nFirstmeal=”+LTRIM(STR(CurVisits.VT_nFirstmeal-1))+”, “+ 
      *** “@VT_nLastmeal=”+LTRIM(STR(CurVisits.VT_nLastmeal-1))+”, “+ 
 
   *
   *  update the row
   *
   lcCmd = “EXEC dbo.USP_Visits_Update “+lcExpression
   lnResult = H2SQLexe(lnHandle, lcCmd)
   IF lnResult <= 0 OR nResult < 0
      *
      *  error handling finale. h2sqlexe already reported the problem to the user.
      *    often it’ll be a usage error, like overlapping dates between 2 visits.
      *
      this.cancelVisits(lnVTID)
      RETURN .F.
   ENDIF
 
   *
   *  now retain the latest field values in the carry forward parameter block
   *
   this.savecarry(toBlock)
 
   *
   *  Above, we just did a full save for the EDIT to this pilgrim’s Visit.
   *    Now we check whether date changes occurred AND the pilgrims is a member of a group.
   *    If so, we ask whether to propagate the date change(s) to other members.
   *
   *  This is new logic for huware 2.1, added 17Nov07, enhanced 03dec07 with the call
   *    to get bookings of other group members auto-updated. Then shifted up 04Jan08 so that 
   *    it only applies if the visit is being edited.
   *
   IF (this.lDtChgStart OR this.lDtChgDepart) AND curVisits.VT_GPID > 0
      IF IDYES = MESSAGEBOX(“Do you want to apply the updated Ultstart/Ultdepart date(s) “+ ;
            “to other members of the same Group (“+TRIM(curGroups.GP_cName)+ ;
            “)?”,MBQYN,”Optional Group Update”)
         *
         *  prepare the SQL command parameter string. Modified 06jan19 to add ID and date parms
         *
         lcCmd = “EXEC dbo.USP_Groups_Autocorrect”+ ;
            ”  @VT_ID=”+LTRIM(STR(curVisits.VT_ID))+ ;
            “, @VT_GPID=”+LTRIM(STR(curVisits.VT_GPID))+ ;
            “, @VT_cIDupdate='”+lcName+”‘ “+ ;
            “, @VT_dUpdate='”+lcDatetime+”‘ “
         IF this.lDtChgStart
            lcCmd = lcCmd + ;
               “, @VT_dOrigStart='”+STUFF(STUFF(DTOS(curVisits.VT_dOrigUltstart),7,0,’.’),5,0,’.’)+”‘”+ ;
               “, @VT_dNewStart='”+STUFF(STUFF(DTOS(curVisits.VT_dUltstart),7,0,’.’),5,0,’.’)+”‘”
         ENDIF
         IF this.lDtChgDepart
            lcCmd = lcCmd + ;
               “, @VT_dOrigDepart='”+STUFF(STUFF(DTOS(curVisits.VT_dOrigUltdepart),7,0,’.’),5,0,’.’)+”‘”+ ;
               “, @VT_dNewDepart='”+STUFF(STUFF(DTOS(curVisits.VT_dUltdepart),7,0,’.’),5,0,’.’)+”‘”
         ENDIF
         *
         *  ask about the bookings. this is a new option added 9jan12 at Pat’s request. Until we
         *    added this, the group’s visit dates were optionally being modified to match this visits
         *    record, but the group’s booking dates were being left as is (only the projected end
         *    date was being modified). Now if the visit is extended, we can extend the booking date
         *    of the group to match. If this appears no to have worked it will be because of the 
         *    restrictio that we only modify a booking record whose end date matched the original
         *    departure date. This feature will work for simple bookings. the group was booked for 
         *    MPR for 3 daye. They ask to stay a 4th day. all group members have matching start
         *    and depart dates AND all have only 1 booking record.
         *
         IF this.lDtChgDepart ;
            AND DTOS(curVisits.VT_dOrigUltdepart) < DTOS(curVisits.VT_dUltdepart) ;
            AND IDYES = MESSAGEBOX(“Do you want to use the extended Ultdepart date “+ ;
            “to extend the booking end dates (where possible) of other members of the same Group (“+ ;
            TRIM(curGroups.GP_cName)+”)?”,MBQYN,”Optional Group BOOKINGS Update”)
            *
            *  supply new optional parameter created 9jan12
            *
            lcCmd = lcCmd + “, @BookingOption = 1”
 
         ENDIF
         *
         *  submit the SQL command string
         *
         lnResult = H2SQLexe(lnHandle, lcCmd)
         IF lnResult <= 0 OR nResult < 0
            *
            *  error handling finale. h2sqlexe already reported the problem to the user.
            *
            =MESSAGEBOX(“Unable to update DATE(s) for other pilgrims in this Group. You’ll have to do it manually.”,MBEO,”Please Note!”)
         ENDIF
      ENDIF
   ENDIF
ENDCASE
 
*
*  indicate no date(s) edited (since the Save)
*
this.lDtChgStart = .F.
this.lDtChgDepart = .F.
 
*
*  refresh the grid so it’ll show the updates to this row. Moved this code down
*    so it applies to both an edit and an add situation, 09Oct06. Also added the ID
*    parameter to ensure that the record we are adding/editing remains highlighted.
*
this.findvisits(lnVTID)
 
RETURN .T.
ENDPROC

…Page3.cntDEvisits.CurrentVisits
Fills the curVisits result set so that Tab 3 will be up to date before the user clicks it

*– Called from the form’s pageactivate method when going directly to the bookings page,  so that we first get set up for the current Visits row of the selected Pilgrim. Whew.
PROCEDURE currentvisits
LOCAL lnCount, lnRec, llFound
 
*
*  we have a newly-selected Pilgrims rcd and we need to set up the 
*    curVisits cursor with that Pilgrim’s Visits records, if any.
*
 
*
*  first look for any/all Visits for the current Pilgrims row
*
lnCount = this.findvisits()     &&  note–calls newvisits(), sets thisform.nVT_ID
IF lnCount = 0
   *
   *  at this point we know there are no visits for this pilgrim. But there’s one
   *    thing we need to do here–acknowledge that this container, with no visits
   *    found or added yet, IS in sync with the current pilgrim. If, instead, 
   *    we fall through to the code below, this setting will be done in the call
   *    to newvisits()
   *
   thisform.nPage3PMID = thisform.nPMID
   RETURN 0
ENDIF
 
*
*  here’s where our logic extends that of findvisits(). We need to determine if
*    among the one or more Visits row(s) there’s a current Visits. Our definition of
*    ‘Current’ in this case is pretty loose and is roughly same as the latest visit.
*
*  Now suppose, for example, someone is about to leave, and has already put in a 
*    new Visits request for their return in 5 weeks. This logic would return 2 records,
*    the current one (VT_dUltdepart > 2 days ago) and the upcoming one (VT_dUltstart 
*    would satisfy the test). We want the earlier of the two. But if the locate fails,
*    we’ll be on the latest, which is not a big problem.
*
 
lnRec = RECNO(“curVisits”)
SELECT curVisits
LOCATE FOR MAX(VT_dUltstart,VT_dApproxarr, VT_dPendarr, VT_dUltdepart, VT_dApproxdep) >= DATE()-2
llFound = FOUND()
IF !llFound
   GOTO TOP
   this.refresh()
ENDIF
 
*
*  If found() we have left the record pointer where it has fallen, so the call to
*    newvists() will pre-choose this Visits row by placing it in the VT… properties.
*  If not found(), we simply assume the user would like to see the latest Visits row,
*    which will be true 99%+ of the time.
*
IF lnRec <> RECNO(“curVisits”)
   this.newvisits()         &&  sets thisform.nVTID for the earliest ‘current’ row
ENDIF
 
RETURN lnCount
ENDPROC

…Page3.cntDEvisits.DeleteVisits
Called from the Delete button on Page 3. This is complicated of course because of handling child rows, because the pilgrim whose visit is to be deleted may be in a group, and to prevent a Delete if the op has already modified some Visits data for the current Visit. “Come on op, do you want to update or delete? If you want to delete then Cancel your changes first, THEN delete.” This is an exception to the principle that we defer all SQL server operations until Save or Cancel is clicked. This takes place AS the Delete Visit button is clicked (after the user confirms).

 
PROCEDURE deletevisits
LOCAL lnID, lnHandle, lcCmd, lcName, lcTemp, lcDatetime
PRIVATE nResult
nResult = 0
 
 
*
*  note–this logic departs from the wait-for-the-user-to-click-Save approach of
*    all other code for this form. So that’s why we need a special test for the
*    read-only form property, ilNodataentry. Otherwise output/save logic is already
*    blocked. See the form’s shelladditionalinit().
*
IF thisform.ilNodataentry
   RETURN .T.
ENDIF
 
*
*  make sure a row is highlighted. Always true unless the grid is empty
*
IF EOF(“curVisits”) OR EMPTY(curVisits.VT_ID)
   =MESSAGEBOX(“There must be a visit highlighted in the grid”,MBEO,”Nothing to do”)
   RETURN .T.
ENDIF
lnID = curVisits.VT_ID
 
*
*  refuse to delete if changes are pending
*
IF thisform.getmode() <> “D” OR thisform.lModvisits
   =MESSAGEBOX(“First you must click Save or Cancel to deal with changes already made so far.”,MBEO,”Save/cancel pending”)
   RETURN .T.
ENDIF 
 
*
*  13dec18 new mods to include userid and date in the parameters. there are
*    corresponding mods in the stored procedures and triggers for hPilgrims.
*
lcName = IIF(TYPE(“oApp.cUsername”) = “C”, ;
   oApp.cUsername, “Unknown/Unknown”)
 
*
*  25dec18 modified this to include the time in the character expression for SQL Server
*
lcTemp = DTOC(DATETIME())
lcDatetime = SUBSTR(lcTemp,7,4)+’.’+SUBSTR(lcTemp,4,2)+’.’+LEFT(lcTemp,2)+” “+TIME()
 
*
*  ask official permission
*
IF IDNO = MESSAGEBOX(“Delete the visit dated “+ ;
      DTOC(MAX(curVisits.VT_dUltstart,curVisits.VT_dApproxarr,curVisits.VT_dPendarr))+ ;
      ” thru “+DTOC(MAX(curVisits.VT_dUltdepart,curVisits.VT_dApproxdep))+”?”+CR2+ ;
      “Note–this action will also delete any Bookings and Room Assignments associated with this Visit.”+CR+ ;
      “p.s.: This will not be easy to undo/reconstruct!”,MBQYN,”CAREFUL NOW–Confirm Visit Removal”)
   RETURN .T.
ENDIF
 
*
*  ok, delete the current row in hVisits. We’re counting on the CASCADE setting being in place to
*    delete associated child records. Note that our triggers will record all the details of the
*    rows this would remove.
*
*  get connection handle
*
lnHandle = oLib.oConnectionSVC.GetSingleConnectHandle()
IF lnHandle <= 0
   =MESSAGEBOX(“Unable to establish contact with the SQL Server computer”,MBEO,”Connectivity Problem”)
   RETURN 0
ENDIF
 
*
*  create and execute the lookup command
*
*  new feature added 25nov08. option to delete all other members of the party. often all of a party
*    cancel at once…
*
IF curVisits.VT_GPID > 0
   *
   *  this visit is in a group
   *
   IF IDNO = MESSAGEBOX(“This pilgrim whose Visit you are deleting is a member of a group “+ ;
         “(“+TRIM(curGroups.GP_cName)+”).”+CR2+ ;
         “Do you want to delete ALL members of the group?”, ;
         MBQYN,”CAREFUL NOW–Optional Group Delete”)
      *
      *  delete the row. 01jan19 added ‘-‘ prefix on the ID so the trigger can know
      *    to use this ID as the one in hChanges
      *
      lcCmd =  “EXEC dbo.USP_Visits_Delete “+ ;
         “@nResult=0, “+ ;
         “@VT_ID = “+LTRIM(STR(lnID))+”, “+;
         “@VT_GPID = 0, “+;
         “@VT_cIDupdate=’-“+lcName+”‘, “+;
         “@VT_dUpdate='”+lcDatetime+”‘ “
   ELSE
      *
      *  14nov09 added this re-confirmation/warning message to prevent accidental mass
      *    delete. Operators, as reported by Meredi, just say Yes to everything and then
      *    regret it afterward. Will one more Yes opportunity stop this laziness about
      *    actually reading dialogs?
      *
      IF IDYES = MESSAGEBOX(“Are you ABSOLUTELY SURE you want to delete all members of group “+ ;
         “(“+TRIM(curGroups.GP_cName)+”)?”, ;
         MBQYN,”Last Chance to Read the Message!!!”)
         *
         *  here’s the point of no return for group delete…20feb19 fixed this
         *    by removing an unbalanced single quote in the GPID parameter
         *
         lcCmd =  “EXEC dbo.USP_Visits_Delete “+ ;
            “@nResult=0, “+ ;
            “@VT_ID = “+LTRIM(STR(lnID))+”, “+;
            “@VT_GPID = “+LTRIM(STR(curVisits.VT_GPID))+”, “+;
            “@VT_cIDupdate=’-“+lcName+”‘, “+;
            “@VT_dUpdate='”+lcDatetime+”‘ “
      ELSE
         *
         *  only delete THIS PILGRIM’S visit, after reconfirming individual visit delete
         *
         IF IDYES = MESSAGEBOX(“Delete (just) the individual’s visit dated “+ ;
            DTOC(MAX(curVisits.VT_dUltstart,curVisits.VT_dApproxarr,curVisits.VT_dPendarr))+ ;
            ” thru “+DTOC(MAX(curVisits.VT_dUltdepart,curVisits.VT_dApproxdep))+”?”+CR2+ ;
            “Note–this action will also delete any Bookings and Room Assignments associated with this Visit.”+CR+ ;
            “p.s.: This will not be easy to undo/reconstruct!”,MBQYN,”CAREFUL NOW–Confirm Visit Removal”)
 
            *
            *  14nov09 added this re-reminder about the individual after use said no to the
            *    new warning message
            *
            lcCmd =  “EXEC dbo.USP_Visits_Delete “+ ;
               “@nResult=0, “+ ;
               “@VT_ID = “+LTRIM(STR(lnID))+”, “+;
               “@VT_GPID = 0, “+;
               “@VT_cIDupdate=’-“+lcName+”‘, “+;
               “@VT_dUpdate='”+lcDatetime+”‘ “
         ELSE
            *
            *  14nov09 changed. here the user said yes to the individual, yes to the group,
            *    then no to the failsafe reminder requested by Meredi for the group delete,
            *    then no to the re-reminder about the individual delete.
            *
            RETURN .T.
         ENDIF
      ENDIF
   ENDIF
ELSE
   *
   *  normal case–not a member of a Group
   *
   lcCmd =  “EXEC dbo.USP_Visits_Delete “+ ;
      “@nResult=0, “+ ;
      “@VT_ID = “+LTRIM(STR(lnID))+”, “+;
      “@VT_GPID = 0, “+;
      “@VT_cIDupdate=’-“+lcName+”‘, “+;
      “@VT_dUpdate='”+lcDatetime+”‘ “
ENDIF
 
*
*  after user agreement, perform the requested action…
*
*_CLIPTEXT = lcCmd
*IF VERSION(2)!=0
*RETURN
*ENDIF
lnResult = H2SQLexe(lnHandle, lcCmd)
IF lnResult <= 0
   *
   *  delete failed. !!! handle this error gracefully
   *
   =messagebox(“The requested delete did not work. Database communication problem? Conflict with another user?”,MBEO,”Database problem”)
   this.findvisits()
   SELECT curVisits
   RETURN .T.
ENDIF
 
*
*  delete from the database worked. Now also delete from the cursor? Actually,
*    we’ll use our find method to re-run the query
*
this.findvisits()
SELECT curVisits
 
RETURN .T.
ENDPROC

…Page3.cntDEvisits.CancelVisits
When the op clicks Cancel the cnacelaction callls this method to handle ust the Visits resetting process. Separately calls CancelBookings and CancelRoom.

PROCEDURE cancelvisits
LPARAMETERS tnID
*
*  we have been called from saveVisits() (only if it failed) or from
*    the form’s CancelAction() method to clean up, by restoring the
*    Visits cursor contents to the values reflected in SQL Server.
*
LOCAL ldStart, ldEnd, lnID
 
IF PCOUNT() < 1
   tnID = 0
ENDIF
 
*
*  remember the current record so we can TRY to point to it again 
*    –see analysis of the problem, above. Naturally as of 31sMay06 the
*    current row in curVisits could be an added rcd. If so, when we do
*    the Cancel operation it’ll disappear.
*
ldUltstart = {//}
ldUltdepart = {//}
IF tnID = 0        &&  (no need for this if caller supplied the primary key)
   lnID = curVisits.VT_ID
   ldUltstart = curVisits.VT_dUltstart
   ldUltdepart = curVisits.VT_dUltdepart
endif
 
*
*  do the retrieval of current SQL Server info
*
this.findVisits()
 
*
*  now restore the rcd pointer
*
SELECT curVisits
LOCATE FOR VT_ID = IIF(tnID=0,lnID,tnID)
IF !FOUND()
   IF !EMPTY(ldUltstart)
      LOCATE FOR VT_dUltstart = ldUltstart
      IF !FOUND()
         IF !EMPTY(ldUltdepart)
            LOCATE FOR VT_dUltdepart = ldUltdepart
            IF !FOUND()
               GOTO top
            ENDIF
         ELSE
            GOTO TOP
         ENDIF
      ENDIF
   ELSE
      IF !EMPTY(ldUltdepart)
         LOCATE FOR VT_dUltdepart = ldUltdepart
         IF !FOUND()
            GOTO top
         ENDIF
      ELSE
         GOTO TOP
      ENDIF
   ENDIF
ENDIF
 
*
*  presume we moved things around
*
this.newvisits()
 
RETURN .T.
ENDPROC

…Page3.cntDEvisits.CheckVisits
See checkvt method below

PROCEDURE checkvisits
     RETURN this.cmdCheckvt.Click(.T.)
ENDPROC

…Page3.cntDEvisits.cmdCheckVT.Click
Before we allow a Visits row to be saved we do this integrity check. This is called directly by clicking CheckVisits on the page but also (much more often) from SaveVisits which you can read above.

PROCEDURE cmdcheckvt.Click
*
*  comparable to savebookings in that container, but here we only check 
*    the current row, since we don’t allow multiple unsaved Visits rows.
*
LPARAMETERS tlFromsave
local lnRec, lnSel, lcMsg
 
*
*  optional parameter if set to .t. means we were called from savevisits(),
*    in which case we want to alter the error message text a little…
*
IF pcount() < 1 OR TYPE(“tlFromsave”) <> “L”
   tlFromsave = .F.
ENDIF
 
SELECT curVisits
lnErr = 0
lcMsg = CHR(13)
 
*
*  make sure dates are supplied
*
IF EMPTY(curVisits.VT_dUltstart) OR EMPTY(curVisits.VT_dUltdepart)
   lnErr = lnErr+1
   lcMsg = lcMsg + “Prob: Visit ultimate start and depart dates are required”+CHR(13)
ELSE
   IF curVisits.VT_dUltstart > curVisits.VT_dUltdepart
      lnErr = lnErr+1
      lcMsg = lcMsg + “Prob: Visit ultimate start or depart date error”+CHR(13)
   ENDIF
ENDIF
 
*
*  report the results
*
DO CASE
CASE lnErr = 0 AND !tlFromsave
   =MESSAGEBOX(“Visit looks ok”,64,”So far so good”)
 
CASE lnErr > 0 AND !tlFromsave
   =MESSAGEBOX(“Visit needs work:”+CHR(13)+lcMsg,MBEO,”Note Visit issues”)
 
CASE lnErr > 0 AND tlFromsave
   =MESSAGEBOX(“Visit problem(s):”+CHR(13)+lcMsg,MBEO,”Unable to save changes”)
 
OTHERWISE
   *
   *  no message if no error AND we are doing a save
   *
ENDCASE
 
RETURN (lnErr=0)
ENDPROC

…Page3.cntDEvisits.SaveCarry
There is a SaveCarry for Tabs 2,3,4. It retains fields that we will auto-fill for the next Add Pilgrim only if the op uses a Right Click on the Add Pilgrim next. Used often. Improves accuracy.

*– support for carry forward feature. called from savevisits()
PROCEDURE savecarry
*
*  this method retains the just-saved values of fields that match our parameter block.
*    Thus on the Next Add of a visit, these data will be available to include automatically
*    to be carried forward) without re-typing them.
*
LPARAMETERS toBlock
LOCAL ln, lnrows, lcField
 
lnRows = ALEN(toBlock.aParameters,1)
FOR ln = 1 TO lnRows
   lcType = toBlock.aParameters(ln,PB_FIELD_TYPE)
 
   *
   *  special code added for Groups 04jan08. This pre-charges a separate
   *    display field with the group name. this is handled in frmH2carry.vcx
   *
   lcDispfield = toBlock.aParameters(ln,PB_FIELD_DispName)
   IF !EMPTY(lcDispfield)
      luValue = &lcDispfield      &&  retrieve value to display
      toBlock.aParameters(ln,PB_FIELD_DispVal) = luValue
   ENDIF
 
   *
   *  standard code saves a field which, for groups, is GP_ID. for other
   *    items, it’s both the display value and the value to be carried forward,
   *    e.g. the pilgrim’s last name, email address, city.
   *
   lcField = toBlock.aParameters(ln,PB_FIELD_NAME)
   luValue = &lcField
   if !EMPTY(luValue)
      toBlock.aParameters(ln,PB_FIELD_VALUE) = luValue
   ELSE
      toBlock.aParameters(ln,PB_FLAG) = PB_IGNORE
   ENDIF
ENDFOR
 
RETURN .t.
ENDPROC

…Page3.cntDEvisits.ClearCarry
Similar logic on tabs 2 and 4. Once the op Left Clicks Add Pilgrim old retained data is cleared out for safety.

*– clear carried forward visit data when we begin to add a visit without using the carry forward feature.
PROCEDURE clearcarry
lparameters toBlock
 
*
*  need to clear the carry forward data for pilgrim visits, since the new pilgrim visit
*    is NOT using carry forward and we don’t want the last guy’s data to clobber what 
*    we are about to enter when the user comes to this.savecarry()
*
lnRows = ALEN(toBlock.aParameters,1)
FOR ln = 1 TO lnRows
   lcField = toBlock.aParameters(ln,PB_FIELD_NAME)
   lcType = TYPE(lcField)
   *
   *  note–pretty sure all field values are Character…
   *
   DO CASE
   CASE lcType = “C”
      STORE “” TO toBlock.aParameters(ln,PB_FIELD_VALUE)
   CASE lcType = “N”
      STORE 0 TO toBlock.aParameters(ln,PB_FIELD_VALUE)
   CASE lcType = “L”
      STORE .F. TO toBlock.aParameters(ln,PB_FIELD_VALUE)
   ENDCASE
   *
   *  displayvalue is ALWAYS character fer sher
   *
   STORE “” TO toBlock.aParameters(ln,PB_FIELD_DISPVAL)
ENDFOR
 
RETURN .T.
ENDPROC

…Page3.cntDEvisits.ClearCursor
Empty out the curVisits result set on a change to a new pilgrim or an Add Visit

PROCEDURE clearcursor
LOCAL lnSel, ln, lok
 
lnSel = SELECT()
SELECT CurVisits
COUNT FOR !DELETED() TO ln
IF ln > 0
   lok = .t.
   TRY 
      ZAP           &&  ZAP of our cursor works, but DELETE FROM below was leaving a row behind
   CATCH            &&    some of the time(!)
      lok = .F.
   ENDTRY
   IF !lok
      DELETE FROM curVisits WHERE .T.
   ENDIF
ENDIF
 
SELECT (lnSel)
RETURN (ln > 0)
ENDPROC

…Page3.cntDEvisits.BeforeRowColChange
Manage moving around in the Visits navigation grid on Tab 3. Handles the case that op is trying to go to a different Visit while changes to the current Visit have not yet been saved.

PROCEDURE grdnav.BeforeRowColChange
LPARAMETERS nColIndex
 
IF this.RowColChange = 1 OR this.RowColChange = 3
   IF thisform.getmode() <> “D”
      =MESSAGEBOX(“Please Save or Cancel before moving to a different visit”,MBEO,”Save Pending”)
      NODEFAULT
      RETURN .T.
   ENDIF
ENDIF
 
RETURN DODEFAULT(nColIndex)
ENDPROC

…Page3.cntDEvisits.AllowNavigation
Comments

PROCEDURE grdnav.allownavigation
 
*
*  XXFWFRM.VCX/grdNavigate::AllowNavigation()
*
*
*  the idea is to deny record navigation if THISFORM 
*  is in Add/Edit mode
*
*  called from:
*    THIS.When()
*    THIS.UIEnable()
*    XXFWFRM.VCX/txtGridNavFind.When()
*    XXFWFRM.VCX/tbrSCADONav.cmdFind.Click()
*
*
*  Our custom version, in which we don’t interfere with navigation to a different Visits row
*    if only the Pilgrims table has been modified. In that case, the Save/Cancel can remain
*    pending. A variation of this code also appears in the grdnav on Page 1, but only so
*    we can custom tailor the message. The logic of allownavigation() the original is fine there.
*
LPARAMETERS tlMDown  
 
***RETURN DODEFAULT(tlMdown)
RETURN .T.
 
 
LOCAL lcMode, llRetVal
lcMode = THISFORM.GetPProp(“icMode”)
 
llRetVal = .t.
IF lcMode <> “DEFAULT” ;
      AND (thisform.lModVisits OR thisform.lModBookings OR thisform.lModrooms) AND tlMDown
   *
   *  THISFORM.GetMode() = “ADD” or “EDIT”
   *
   =MESSAGEBOX(“First save changes for this Visit / Booking / Room”,MBEO,”Save/Cancel Pending (v)”)
   llRetVal = .f.
ENDIF
 
RETURN llRetVal
ENDPROC

…Page3.cntDEvisits.SelectionMade
Runs when user has clicked a Visit on the Tab 3 navigation grid.

PROCEDURE grdnav.selectionmade
LOCAL lok
lok = DODEFAULT()
 
*
*  if user has made a selection, the properties need to be populated from
*    the fields of the chosen record set row.
*
IF lok
   lok = this.Parent.newvisits()   &&  manages thisform.nVTID, .nBKID
ENDIF 
 
RETURN lok
ENDPROC

…Page3.cntDEvisits.grdnav.Click
Treats a single click in the navigation gris as a selection (no double click needed)

PROCEDURE grdnav.Click
DODEFAULT()      &&  same as saying…   grdBase::Click()
 
THIS.SelectionMade()
ENDPROC

…Page3.cntDEvisits.grdnav.setcolumncontrolsources
On startup sets up the navigation grid’s properties

PROCEDURE grdnav.setcolumncontrolsources
lcRS = “curVisits”
 
*
*  assignment of the rowsource is defered until we have chosen a Pilgrims row.
*    we need to verify that we have a row source before we can set the controlsources…
*
IF USED(“curVisits”)
   this.column1.ControlSource = lcRS+”.VT_lAdd”
   this.column2.ControlSource = lcRS+”.VT_dUltstart”
   this.column3.ControlSource = lcRS+”.VT_dUltdepart”
   this.column4.ControlSource = lcRS+”.FA_cCode”
   this.column5.ControlSource = lcRS+”.VT_lArrived”
   this.column6.ControlSource = lcRS+”.VT_lComplete”
ENDIF
 
this.column1.Width =44
this.column2.Width = 110
this.column3.Width = 110
this.column4.Width = 100
this.column5.Width = 84
this.column6.Width = 84
this.column1.header1.caption = “New?”
this.column2.header1.caption = “Ultstart”
this.column3.header1.caption = “Ultdepart”
this.column4.header1.caption = “Facil Req”
this.column5.header1.caption = “Arrived”
this.column6.header1.caption = “Complete”
 
RETURN .T.
ENDPROC

…Page3.cntDEvisits.SetCaption
On Tab 3 this adjusts the caption giving pilgrim name

*– maintain the caption of the lblTitle control
PROCEDURE setcaption
*
*  no parameters needed
*
LOCAL lcName
lcName = TRIM(curPilgrims.PM_cLast)+ “, ” + ;
TRIM(curPilgrims.PM_cFirst)+ ;
IIF(EMPTY(curPilgrims.PM_cFirst),TRIM(” “+curPilgrims.PM_cMiddle),””) + ;
IIF(!EMPTY(curPilgrims.PM_cNicknm),” (“+TRIM(curPilgrims.PM_cNicknm)+”)”,””)
 
this.lblTitle.Caption = “Visit(s) for: “+lcName
RETURN .T.
ENDPROC

…Page3.cntDEvisits.cmdAddVisit.Click
op clicked the Add Visit button. NOTE Also see the next method which is how we do things a bit differently if the op RIGHT clicks the Add Visit button.

PROCEDURE cmdaddvisit.Click
*
*  31May06 new logic. If we already have a new Visit, require Save before we add another
*
SELECT curVisits
LOCATE FOR VT_ID=-1
IF FOUND()
   =MESSAGEBOX(“You already have a new visit record which must be Saved before you may add another.”, ;
      MBEO,”Save is Pending”)
   RETURN .T.
ENDIF
 
*
*  enforce saving any mods at visits or bookings level. If only Pilgrims table has
*    been altered we can proceed.
*
LOCAL lok, lcMsg
lcMsg = “”
IF thisform.lModvisits OR thisform.lModbookings OR thisform.lModrooms
   lcName = TRIM(curPilgrims.PM_cFirst)+TRIM(” “+curPilgrims.PM_cLast)
 
   lcMsg = “First you must save the changes you have made:”+CR2
   IF thisform.lModvisits AND thisform.nVTID > 0
      lcMsg = lcMsg+”Edits to the “+DTOC(MAX(curVisits.VT_dUltstart,curVisits.VT_dApproxarr,curVisits.VT_dPendarr))+ ;
         ” thru “+DTOC(MAX(curVisits.VT_dUltdepart,curVisits.VT_dApproxdep))+” visit for “+lcName+CR
   ELSE
      IF thisform.lModvisits
         lcMsg = lcMsg+”New visit, “+DTOC(MAX(curVisits.VT_dUltstart,curVisits.VT_dApproxarr,curVisits.VT_dPendarr))+ ;
            ” thru “+DTOC(MAX(curVisits.VT_dUltdepart,curVisits.VT_dApproxdep))+” for “+lcName+CR
      ENDIF
   ENDIF
 
   IF thisform.lModbookings AND thisform.nBKID > 0
      lcMsg = lcMsg+”Edits to the “+DTOC(MAX(curVisits.VT_dUltstart,curVisits.VT_dApproxarr,curVisits.VT_dPendarr))+ ;
         ” thru “+DTOC(MAX(curVisits.VT_dUltdepart,curVisits.VT_dApproxdep))+” visit for “+lcName
   ELSE
      IF thisform.lModbookings
         lcMsg = lcMsg+”New booking: “+DTOC(MAX(curBookings.BK_dStart,curVisits.VT_dApproxarr,curVisits.VT_dPendarr))+ ;
            ” thru “+DTOC(MAX(curVisits.VT_dUltdepart,curVisits.VT_dApproxdep))+” for “+lcName
      ENDIF
   ENDIF
 
   *
   *  explain what needs to be saved
   *
   =MESSAGEBOX(lcMsg,MBEO,”Please Note”)
   IF TYPE(“thisform.cmdSave”)=”O”
      thisform.cmdSave.setfocus()
   ENDIF 
   RETURN .F.
ENDIF
 
*
*  note this is not a cmddataaction button that would trigger an append blank to the
*    thisform.icMainviewalias table. All ‘add’ logic is done here. We must clear out the
*    curVisits cursor, set flags, fix up the thisform.nVTID and thisform.nPage3PMID 
*    to indicate an Insert is pending.
*
***this.parent.newvisits(.T.)        &&  set up for the new empty row we add below
 
*
*  here we need to empty out the curBookings and curRoom cursors if they contain any
*    data. If they do have rows, they relate to an old visit in curVisits (the current row,
*    if there is one) and not to the new row we are about to add. New 04Jan08
*
thisform.pilgrimreset(3)       &&  the ‘3’ parameter means we’re calling from page 3 (visits)
 
 
*
*  31May06 drastic logic change in which we now DO add a row to curVisits on an Add.
*    Previously we only cleared the properties, which lead to problems in adding
*    a new booking for a new visit, since the bookings container wrongly thought it
*    could find visit info in curvisits, although this info was only in properties.
*
 
*
*  override some of the settings from newvisits(). In particular,
*    pre-select the first primary facility.
*
STORE -1 TO thisform.nVTID   &&  this means an added record is pending
SELECT RS_Facilities
LOCATE FOR RS_Facilities.FA_lPrimary=1
IF FOUND()        &&  should ALWAYS be TRUE
   *
   *  if this code looks like overkill, it may be. But trying to get the combo to
   *    bind properly and behave ok was days of hassle. We ended up (see savevisits())
   *    using the curVisits.BK_FAIDreq rather than this.(parent.)VTFAIDreq to work
   *    around what appears to be a VFP 9.0 bug.
   *
   STORE RS_Facilities.FA_ID TO this.Parent.cboFacility.Value
   STORE RS_Facilities.FA_cCode TO this.Parent.cboFacility.Displayvalue
 
   *
   *  create the new row in curVisits
   *
   INSERT INTO curVisits (VT_ID, VT_FAIDreq, FA_cCode, VT_lAdd) ;
      VALUES (-1, RS_Facilities.FA_ID, RS_Facilities.FA_cCode, .T.)
 
ELSE
   *** new correct logic, part 2
   INSERT INTO curVisits (VT_ID, VT_lAdd) VALUES (-1, .T.)
ENDIF
 
*
*  for now (june06 redesign) we no longer propagate pilgrim info into curVisits, curBookings 
*    and curRoom. Instead we just have to look back into the proper cur___ file to find
*    relevant info. So, for example, the setcaption() methods will get pilgrim name from
*   the source–curPilgrims.
*
*SELECT curVisits
*   replace PM_cLast WITH lo2.PMcLast, PM_cFirst WITH lo2.PMcFirst, ;
*      PM_cMiddle WITH lo2.PMcMiddle, PM_cNicknm WITH lo2.PMcNicknm, ;
*      PM_cGender WITH lo2.PMcGender IN curVisits
 
*
*  need to clear the carry forward data for visits, since this pilgrim is NOT being
*    carried forward and we don’t want the last guy’s data to clobber what we are about
*    to enter.
*
loBlock = thisform.oVTparamblock
this.parent.clearcarry(loBlock)
 
 
thisform.editaction()                    &&  really bring out the Save and Cancel buttons.
thisform.lModvisits = .T.                &&  make sure thisform.Saveaction knows we have i/o pending.
this.Parent.Refresh()
 
this.Parent.txtUltstart.SetFocus()
 
RETURN .T.
ENDPROC

…Page3.cntDEvisits.cmdAddVisit.RIGHTClick
Do the usual Click processing but then also manage the retained info for the new Visit.

PROCEDURE cmdaddvisit.RightClick
LOCAL lnRows, lok, lcMsg, lcName, loForm, loBlock, llEmpty, lcField, luValue
 
*
*  31May06 new logic. If we already have a new Visit, require Save before we add another
*
SELECT curVisits
LOCATE FOR VT_ID=-1
IF FOUND()
   =MESSAGEBOX(“You already have a new visit record which must be Saved before you may add another.”, ;
      MBEO,”Save is Pending”)
   RETURN .T.
ENDIF
 
*
*  enforce saving any mods at visits or bookings level. If only Pilgrims table has
*    been altered we can proceed.
*
lcMsg = “”
IF thisform.lModvisits OR thisform.lModbookings OR thisform.lModrooms
   lcName = TRIM(curPilgrims.PM_cFirst)+TRIM(” “+curPilgrims.PM_cLast)
 
   lcMsg = “First you must save the changes you have made:”+CR2
   IF thisform.lModvisits AND thisform.nVTID > 0
      lcMsg = lcMsg+”Edits to the “+DTOC(MAX(curVisits.VT_dUltstart,curVisits.VT_dApproxarr,curVisits.VT_dPendarr))+ ;
         ” thru “+DTOC(MAX(curVisits.VT_dUltdepart,curVisits.VT_dApproxdep))+” visit for “+lcName+CR
   ELSE
      IF thisform.lModvisits
         lcMsg = lcMsg+”New visit, “+DTOC(MAX(curVisits.VT_dUltstart,curVisits.VT_dApproxarr,curVisits.VT_dPendarr))+ ;
            ” thru “+DTOC(MAX(curVisits.VT_dUltdepart,curVisits.VT_dApproxdep))+” for “+lcName+CR
      ENDIF
   ENDIF
 
   IF thisform.lModbookings AND thisform.nBKID > 0
      lcMsg = lcMsg+”Edits to the “+DTOC(MAX(curVisits.VT_dUltstart,curVisits.VT_dApproxarr,curVisits.VT_dPendarr))+ ;
         ” thru “+DTOC(MAX(curVisits.VT_dUltdepart,curVisits.VT_dApproxdep))+” visit for “+lcName
   ELSE
      IF thisform.lModbookings
         lcMsg = lcMsg+”New booking: “+DTOC(MAX(curBookings.BK_dStart,curVisits.VT_dApproxarr,curVisits.VT_dPendarr))+ ;
            ” thru “+DTOC(MAX(curVisits.VT_dUltdepart,curVisits.VT_dApproxdep))+” for “+lcName
      ENDIF
   ENDIF
 
   *
   *  explain what needs to be saved
   *
   =MESSAGEBOX(lcMsg,MBEO,”Please Note”)
   IF TYPE(“thisform.cmdSave”)=”O”
      thisform.cmdSave.setfocus()
   ENDIF 
   RETURN .F.
ENDIF
 
*
*  note this is not a cmddataaction button that would trigger an append blank to the
*    thisform.icMainviewalias table. All ‘add’ logic is done here. We must clear out the
*    curVisits cursor, set flags, fix up the thisform.nVTID and thisform.nPage3PMID 
*    to indicate an Insert is pending.
*
***this.parent.newvisits(.T.)        &&  set up for the new empty row we add below
 
*
*  here we need to empty out the curBookings and curRoom cursors if they contain any
*    data. If they do have rows, they relate to an old visit in curVisits (the current row,
*    if there is one) and not to the new row we are about to add. New 04Jan08
*
thisform.pilgrimreset(3)       &&  the ‘3’ parameter means we’re calling from page 3 (visits)
 
 
*
*  31May06 drastic logic change in which we now DO add a row to curVisits on an Add.
*    Previously we only cleared the properties, which lead to problems in adding
*    a new booking for a new visit, since the bookings container wrongly thought it
*    could find visit info in curvisits, although this info was only in properties.
*
 
*
*  override some of the settings from newvisits(). In particular,
*    pre-select the first primary facility.
*
STORE -1 TO thisform.nVTID   &&  this means an added record is pending
SELECT RS_Facilities
LOCATE FOR RS_Facilities.FA_lPrimary=1
IF FOUND()        &&  should ALWAYS be TRUE
   *
   *  if this code looks like overkill, it may be. But trying to get the combo to
   *    bind properly and behave ok was days of hassle. We ended up (see savevisits())
   *    using the curVisits.BK_FAIDreq rather than this.(parent.)VTFAIDreq to work
   *    around what appears to be a VFP 9.0 bug.
   *
   STORE RS_Facilities.FA_ID TO this.Parent.cboFacility.Value
   STORE RS_Facilities.FA_cCode TO this.Parent.cboFacility.Displayvalue
 
   *
   *  create the new row in curVisits
   *
   INSERT INTO curVisits (VT_ID, VT_FAIDreq, FA_cCode, VT_lAdd) ;
      VALUES (-1, RS_Facilities.FA_ID, RS_Facilities.FA_cCode, .T.)
 
ELSE
   *** new correct logic, part 2
   INSERT INTO curVisits (VT_ID, VT_lAdd) VALUES (-1, .T.)
ENDIF
 
thisform.editaction()                    &&  really bring out the Save and Cancel buttons.
thisform.lModvisits = .T.                &&  make sure thisform.Saveaction knows we have i/o pending.
 
* ———– Additional code for right-click ———————————-
*
*  Now check for retained field values the user might want to carry forward
*
loForm = thisform.oCarryform
loBlock = thisform.oVTparamblock
 
*
*  new code 04jan08. if no items are given in the block, then skip showing
*    an empty form.
*
llEmpty = .T.
*
*  18mar07 added this test to prevent a crash reported by Irene
*
lnRows = ALEN(loBlock.aParameters,1)
FOR ln = 1 TO lnRows
   IF !EMPTY(loBlock.aParameters(ln,PB_FIELD_VALUE))
      llEmpty = .F.
      EXIT
   ENDIF
ENDFOR
*
*  show eligible fields for carry-forward
*
IF !llEmpty
   loForm.Show(1,loBlock,”VISITS data entry fields you may carry forward”)
ENDIF
 
 
IF lnRows>0   &&  detect whether there IS anything to carry forward…
   FOR ln = 1 TO lnRows
      IF loBlock.aParameters(ln,PB_FLAG) = PB_CARRYFWD
         *
         *  user checked this field as being for carry forward
         *
         lcField = loBlock.aParameters(ln,PB_FIELD_NAME)
         luValue = loBlock.aParameters(ln,PB_FIELD_VALUE)
 
         DO CASE
         CASE ATC(“VT_GPID”,lcField) > 0
            *
            *  special handling needed for Group carry fwd. We show the group Name
            *    but we need to SET the VT_GPID to the ID number instead.
            *
            IF !EMPTY(lcField) AND !EMPTY(luValue)
               replace &lcField WITH luValue IN curVisits
            ENDIF
            this.Parent.cboGroup.Refresh()
            
         CASE ATC(“FA_CCODE”,lcField) > 0
            *
            *  special handling needed for facility, since we show the FA_cCode
            *    but we need to SET the BK_FAIDdenorm value as well
            *
            IF USED(“RS_Facilities”) AND !EMPTY(luValue)
               SELECT RS_Facilities
               LOCATE FOR RS_Facilities.FA_cCode = TRIM(luValue)
               IF FOUND()
                  REPLACE FA_cCode WITH luValue, ;
                     VT_FAIDreq WITH RS_Facilities.FA_ID IN curVisits
               ENDIF
            ENDIF
         OTHE
            *
            *  normal case–not carrying Facil or Group forward–something else
            *
            IF !EMPTY(lcField) AND !EMPTY(luValue)
               replace &lcField WITH luValue IN curVisits
            ENDIF
         ENDCASE
      ENDIF
   ENDFOR
ENDIF
* ———– Additional code for right-click ———————————-
 
this.Parent.Refresh()
this.Parent.txtUltstart.SetFocus()
 
RETURN .T.
ENDPROC

…Page3.cntDEvisits.cmdMembers.Click
Shows the members of a Group that is selected in the group combo box on Tab 3.

PROCEDURE cmdmembers.Click
LOCAL lcCmd, lnHandle, lnResult
 
*
*  new code 17Nov07 to allow user to display the members of the currently-chosen party
*
IF curVisits.VT_GPID = 0
   WAIT WINDOW “No Group is selected” timeout(1.2)
   RETURN .T.
ENDIF
 
*
*  get connect handle
*
lnHandle = oLib.oConnectionSVC.GetSingleConnectHandle()
IF lnHandle <= 0
   =MESSAGEBOX(“Unable to connect to SQL Server”,MBEO,”Communication Problem”)
   RETURN .T.
ENDIF
 
*
*  ok, time to do the retrieve
*
lcCmd = “EXEC USP_Ret_GroupMembers @tGPID=”+LTRIM(STR(curVisits.VT_GPID))
lnResult = H2SQLexe(lnHandle, lcCmd, “curMembers”)
IF lnResult <= 0
   *
   *  error message will have been reported by h2Sqlexe
   *
   RETURN .T.
ENDIF
IF !USED(“curMembers”)
   RETURN .T.
ENDIF
IF RECCOUNT(“curMembers”) < 1
   WAIT WINDOW “No pilgrims have been added to this Group (yet)” timeout(1)
   USE IN curMembers
   RETURN .T.
ENDIF
 
*
*  display the members in a modal nondata form. user can read, click ok
*    but not change anything…
*
DO FORM frmListmembers WITH curGroups.GP_cName
USE IN curMembers
 
RETURN
ENDPROC

…Page3.cntDEvisits.cmdRomNotes
Opens an edit box popup form to receive optional notes about roommate requests

PROCEDURE cmdromnotes.Click
 
*
*  open an edit form for the notes (because the editbox is too small to
*    show a decent amount of the string).
*
LOCAL lcString
lc = this.Parent.edtRoomnotes.controlsource
lcString = &lc
 
IF this.parent.openmemoform(@lcString,”Edit Notes About Roommates / Room”)
   *
   *  openmemoform returns .T. if lcString was altered
   *
   REPLACE &lc WITH lcString IN curVisits
   this.Parent.edtRoomnotes.Refresh()
ENDIF
 
RETURN .T.
ENDPROC

…Page3.cntDEvisits.opgScope.InteractiveChange
Handles op changing the Scope, usually set to Season Visits, but may be changed to Current Visits (only pilgrims whose visit contains today’s date or All Visits which shows Visit history incl previous season.

PROCEDURE opgscope.InteractiveChange
LOCAL lok, ln, lcRS
lok = DODEFAULT()
 
*
*  dangerous to reset properties when changes are pending
*
IF thisform.getmode() <> “D”
   WAIT WINDOW “Save or cancel first!” timeout(1)
   RETURN .T.
ENDIF 
 
*
*  if user has changed the scope of visits we’ll be displaying,
*    we need to redo the seek and re-populate the properties for flds….
*
ln = this.parent.findvisits()
DO CASE
CASE ln=0
   *
   *  no visits rows found within the chosen scope. When findvisits() fails to find
   *    and rows, it calls newvisits(.T.) for us.
   *
   =MESSAGEBOX(“No current visit was found. Click This Season to see future and past visits.”, MBEO, “Nope…”)
 
CASE ln=1
   *
   *  one visits row found, so prepare to show it in controls of the container
   *
   this.parent.newvisits()
 
OTHERWISE
   *
   *  multiple visits rows found, so pre-select the latest
   *
 
   *
   *  31May06 new logic: if there’s an added visits row, select it, 
   *    otherwise do what we always did
   *
   SELECT curVisits
   LOCATE FOR VT_ID=-1
   
   *
   *  if there isn’t an added visit, highlight the latest one
   *
   IF !FOUND()
      GOTO TOP
   ENDIF
   this.parent.newvisits()      &&  sets thisform.nVTID from curVisits.VT_ID
  
ENDCASE
 
RETURN lok
ENDPROC

…Page3.cntDEvisits.cmdGroups
Comments

PROCEDURE cmdgroups.Click
LOCAL lnID, loBlock, lnRows, lok, lcCmd, lnResult, lcDBF, lnFormerGPID
 
*
*  new code 16Nov07 to allow user to investigate Groups via the picklist form
*    and to change the current Group setting from actions on that form…
*
loBlock = thisform.oGPparamblock
loBlock.aParameters(1,2) = curVisits.VT_GPID
***DO FORM frmPickGroups WITH “Modal”,loBlock
*** changed 27nov09 to the newer fancier form
*** changes 28nov09 to add the new GPID parameter (which I guess I could pass in the parameter
***    block, but (a) I’m lazy and (b) the block was intended for return values…)
 
lnFormerGPID = curVisits.VT_GPID    &&  added this line 15jan10
*
*  01may25 updated the form name to frmUT22groups, the modern version
*
DO FORM frmUT22Groups WITH “Modal”,loBlock   &&  15jan10 reduced to 2 parameters
 
*
*  check parameter block for new/edited group
*
lnRows = ALEN(loBlock.aParameters,1)
IF lnRows = 3    &&  15jan10 changed ‘2’ to ‘3’ here
   *
   *  parameter block has 3 rows so we take it to be valid
   *
   lnID = loBlock.aParameters(1,2)
   ***
   ***  revised 15jan10. I noted that Save/Cancel always shows up when you click Groups
   ***    and the visit is associated with a Group. This even if you changed nothing.
   ***    The old logic is commented out with single *s. New code follows it.
   ***
*   IF lnID > 0
*      replace VT_GPID with lnID IN curVisits
*      this.Parent.cboGroup.InteractiveChange()
*      this.Parent.cboGroup.Refresh()
*   ELSE
*      lnID = 0
*   ENDIF
   *
   *  see if any group changes were made
   *
   llMod = loBlock.aParameters(3,2)   &&  retrieve the new ‘modified’ flag
   *
   *  on return, we have 2 concerns. First, if the GPID changed for THIS pilgrim, we need to present
   *    the Save/Cancel buttons. Second if any (other) change was made, we need to get the page 1 
   *    selection grid refreshed. Here’s why: when you go to frmUTgroup, you are free to put anyone 
   *    into or out of any group, not just this pilgrim. You can make a lot of changes and if the
   *    selection grid on page 1 is, for example, current pilgrims, or pilgrims for a selected
   *    country, the groups changes you have made may aFfect a lot of lines in this page 1 grid.
   *    So, it is important to make it current, or the user may think some serious error has occurred.
   *  finally, if the user has made some change in frmUT22groups it MIGHT affect our combobox in this
   *    container class (ctrDEVisits). So we rebuid in just in case.
   *  and very finally, please note that although we can see at this point that the user is changing
   *    the gp_id of the selected pilgrim/visit at this point, it’s to early to refresh the page 1
   *    grid, because the Save/Cancel buttons hve not yet been acted on.
   *
   IF lnID != lnFormerGPID   &&  First concern: detect any group chg: 00->nnn, nnn->00, OR mmm->nnn
      *
      *  potential problem here. Suppose the operator has created 3 groups in frmutgroup,
      *    named Stephens-A, Stephens-B and Stephens-C. The following line of code presumes
      *    that the current pilgrim-visit should be associated with Stephens-C, since that’s
      *    the last ID # created and then returned in the parameer block. Can we be sure that’s
      *    what the user intended? Usually the answer would be Yes, so I’m leaving like this for
      *    now (20jan10).
      *
      replace VT_GPID with lnID IN curVisits
      this.Parent.cboGroup.InteractiveChange()
      this.Parent.cboGroup.Refresh()
   ENDIF
   IF llMod
 
      *
      *  2nd concern part 2–since a group may have been added in frmPickgroups, we need to rebuild the
      *    contents of our Group names combo box. this code is based on code in the form Load().
      *
      lcCmd = “EXEC dbo.USP_Ret_Groups”
      lnResult = H2SQLexe(thisform.nHandle, lcCmd, “tmpGroups”)
      IF lnResult <= 0 OR !USED(“tmpGroups”)
         =MESSAGEBOX(“Unable to retrieve Groups table from the database”,MBEO,”USP_RET_Groups failure”)
         RETURN .F.
      ENDIF
      SELECT tmpGroups
      lcDBF = DBF()
      lok = .T.
      SELECT curGroups
      TRY 
         ZAP           &&  ZAP of our cursor works, but DELETE FROM below was leaving a row behind
      CATCH            &&    some of the time(!)
         lok = .F.
      ENDTRY
      IF !lok
         DELETE FROM curGroups WHERE .T.
      ENDIF
      INSERT INTO curGroups (GP_cName, GP_ID, GP_cNotes, GP_lActive) ;
         VALUES (‘(no Group)’,0,”,0)
      APPEND FROM &lcDBF
      IF lnID = 0
         GOTO TOP
      ELSE
         LOCATE FOR GP_ID = lnID
      ENDIF
      this.Parent.cboGroup.Refresh()
   ENDIF
   ***  end of new code 15jan10
ENDIF
 
RETURN
ENDPROC

…page3.ctrDEVisits.grdNav.Click
Repeated from Tab3.Methods popup. Makes grdNav respond to a single click

PROCEDURE grdnav.Click
DODEFAULT()      &&  same as saying…   grdBase::Click()
 
THIS.SelectionMade()
ENDPROC

…page3.ctrDEVisits.txtUltstart.InteractiveChange
See comments below

PROCEDURE txtultstart.InteractiveChange
*
*  special treatment for dates, added 17Nov07, because this date edit
*    might need to be applied to the entire PARTY, not just the indiv. pilgrim
*
this.Parent.lDtchgstart = .T.
 
*
*  Every bound control on Page 3 needs to execute this code so we
*    can let the form’s SaveAction() method know that an hVisits row 
*    has been edited or added.
*
this.Parent.flagchange(this)
 
RETURN DODEFAULT()
ENDPROC

…page3.ctrDEVisits.cboFacility.CustomValid
Get set up when op selects a different Facility from the cboFacil combo box on Tab 3.

PROCEDURE cbofacility.customvalid
*
*  now that we have a curVisits row for added records, we want the grid to
*    reflect any values added by the user
*
replace VT_FAIDreq WITH curFac.FA_ID, FA_cCode WITH curFac.FA_cCode IN curVisits
 
return .T.
ENDPROC

…page3.ctrDEVisits.cboFacility.Click

Get set up when op selects a different Facility from the cboFacil combo box on Tab 3. Part 1 of 2 (see the CustomValid method above.

PROCEDURE cbofacility.Click
*
*  Note that the following comments are obsolete. They come from the pre 3June06 era when we put
*    VT info into properties, whereas we now work out of curVisits–much cleaner.
*
*  Click() method was not working properly in the sense that the bound control
*    relationship was failing. E.G. if the chosen facility is 8th on the list 
*    in the combobox and has an FA_ID of 10, the value displays as the choice you made,
*    but the controlsource gets 8, not 10. This persisted even after I removed this.parent.VTFAIDreq
*    as the controlsource. (I bound it to FA_cCode instead of this.parent.VTFAIDreq).
*  Eventually I woke up and set this.boundto = .T. Whew! anyway, we’re bound
*    to the this.parent.VTFAIDreq property, which is the source for writing out the
*    result (see savevisits()). But our grid shows curViaits.FA_cCode so we need to maintain that here.
*
LOCAL lok, lnSel, lcDV, lnFAID
 
lok = DODEFAULT()
 
IF EOF(“curVisits”)          &&  just in case…should never be needed
   RETURN .T.
ENDIF
 
*
*  prep to do a lookup based on the selected FA_cCode value. This works…
*
lcValue = TRIM(this.Displayvalue)
lnValue = this.Value
lnSel = SELECT(0)
SELECT curVisits
IF curVisits.VT_FAIDreq <> lnValue
   *
   *  an actual change in Facility ID has been selected from the combobox.
   *
   SELECT RS_Facilities
   LOCATE FOR FA_cCode = lcValue
   lnFAID = RS_Facilities.FA_ID
   lcFAcCode = RS_Facilities.FA_cCode
 
   *
   *  note–tried a SQL update here, but it moves the rcd ptr to the last rcd while
   *    processing the WHERE clause. This is neater. Prevents saving RECNO() and issuing GOTO.
   *    Main point is that we’re forcing the real FA_ID into our table
   *
   replace FA_cCode with lcFAcCode, VT_FAIDreq WITH lnFaid IN curVisits
ENDIF
SELECT (lnSel)
 
RETURN lok
ENDPROC

…page3.ctrDEVisits.cmdDeleteVisit.Click
Simple way of handling Delete button. See the methods that do the actual work in the Tab3.Methods popup

PROCEDURE cmddelvisit.Click
this.Parent.deletevisits()
this.Parent.Refresh()
 
RETURN .T.
ENDPROC