Category Archives: Intermediate

Working with RE7.95 and phone configuration rules

In v7.95 of RE7, there were some new config business rules introduced which prevent duplicate phone or emails being added to a record.

phone config

I have not really found this to be consistent though. When I use this inside of a constituent record it does not seem to make the slightest difference which setting. I can have to “Email” phone types but no matter which setting is selected above it saves fine. This does seem to work better with phones (as opposed to emails). Although even when I have the setting as “Do not allow record to be saved” it still prompts to if I want to save it.

When I run this through code however I do get an error… sometimes.

If I have a matching phone type but different phone number it seems to work fine most of the time (although once I got an error message). If I have a matching phone type and phone number I will always get the error as shown below. This does not seem to be affected by the options above though.

duplicate phone error

I am wondering wondering if there is a way of controlling the error. If this were working as expected then it should be possible to permit the record to be saved if the options is “Display warning”.

Here is my test code:

 Public Sub TestSavingDuplicatePhones()
 
        Dim constit As New CRecord
        constit.Init SessionContext             

        constit.LoadByField uf_Record_CONSTITUENT_ID, "3"
        
        Dim parent As IBBPhonesParent
        Set parent = constit        

        Dim phone As CConstitPhone
        Dim phones As CConstitPhones
        Dim dataObj As IBBDataObject
        Dim phone2 As CConstitPhone
        Dim dataObj2 As IBBDataObject
       
        For Each phone In parent.phones
            Set dataObj = phone
            If dataObj.Fields(ECONSTITPHONEFIELDS.CONSTIT_PHONES_fld_PHONETYPE) = "Email" Then
                Set phones = parent.phones
                Set phone = phones.Add
                Set dataObj2 = phone
                dataObj2.Fields(ECONSTITPHONEFIELDS.CONSTIT_PHONES_fld_NUM) = "1234"
                dataObj2.Fields(ECONSTITPHONEFIELDS.CONSTIT_PHONES_fld_PHONETYPE) = "Email"                             

                Exit For
            End If
        Next       

        constit.Save        
        constit.Closedown
        Set constit = Nothing   
   End Sub

Adding an Education Record to a Non-Constituent Individual

I have been struggling to find a way to a an education record to a non-constituent individual relationship. There is a Blackbaud knowledgebase article here that outlines how you add an education relationship but it takes a lot for granted. Here is the gist of it:

Dim oEdu As CEducation2
Set oEdu = New CEducation2
oEdu.Init REApplication.SessionContext
With oEdu
   .Fields(EDUCATION2_fld_RECORD_ID) = 678   'Selects the record to add the Education record to
   .Fields(EDUCATION2_fld_SCHOOL_ID) = "Berry College"
   .Save
End With

However where does the 678 come from? For a constituent this is simple. It would be the constituent system id found under the Records_fld_Id field.

However what would it be for a non-constituent individual relationship. After all they can also have education relationship records.

I tried the field Individual2_fld_Id but the error I got back told me that I could not add an education to an organization record! Not very helpful.

In the end, with a bit of trial and error I worked out that you need to put the field Individual2_fld_Relation_id.

This is the id for the corresponding entry in the Records table which consists of both constituents and non-constituents.

Hope this helps somebody.

Working with Phones/Email without addresses in Raiser’s Edge 7.94

Prior to the release of The Raiser’s Edge version 7.94 I was working with all of our products to ensure compatibility with this latest version. If you have not heard (and if you have not heard where have you been hiding), this release of RE removes phones and emails from physical addresses.

When the concept of emails was new, it was possible that you would have an email address tied to your telephone provider or an email address specific to your place of work.  Your phone would either be at home or at work. Having these connnected to your physical address made sense. However it quickly became aparent that with the arrival of mobile phones and of email addresses that were accessible no matter where you were located, phones and emails (and for that matter all types of communication links) should be tied directly to the constituent record and not to a physical address. This is what has happened with the release of RE7.94.

This is a big shift and in terms of developing applications, we have had to allow for both possibilities so that our programs are compatible with users still on 7.93 and below and those that have made the leap over to 7.94.

The good news is that the old way of doing things still works in 7.94. You can still access phones via the CConstitAddress.Phones collection for an address. However you will probably want to access them how they are intended… Free from addresses.

This is done using the new interface IBBPhonesParent. This is implemented by CRecord, CIndividual2 and COrganization2. The collection of phones is a CConstitPhones object which contains the usual methods. You can iterate the collection to give you one CConstitPhone object but here is the problem.

For reasons that I don’t fully understand (I was told due to binary compatibility reasons) there are no properties or methods on the CConstitPhone object. Instead you have to convert this object to an IBBDataObject in order to access the Fields property. This is a real pain but to save myself some trouble I put together two extension methods for the CConstitPhone object which does this for me. (Unfortunately extension properties do not exist so that is why I cannot simply create an exact corresponding Fields properties. Those working with C# will be familiar with this as there is not a Fields property but rather  get_Fields and set_Fields methods)

<System.Runtime.CompilerServices.Extension()> _
Public Function Fields(ByVal phone As CConstitPhone, fieldConstant As ECONSTITPHONEFIELDS) As Object
   Dim dataobject As IBBDataObject = CType(phone, IBBDataObject)
   Return dataobject.Fields(fieldConstant)
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Sub Fields(ByVal phone As CConstitPhone, fieldConstant As ECONSTITPHONEFIELDS, value As Object)
    Dim dataobject As IBBDataObject = CType(phone, IBBDataObject)
    dataobject.Fields(fieldConstant) = value
End Sub

With these extension method you can simply access the Fields methods on a CConstitPhone object in almost the same way as you would with other objects.

So here is an example of creating a new phone on a constituent record using the above extension code.

Dim constit As CRecord = GetConstituent()
Dim phonesParent As IBBPhonesParent
Dim phones As CConstitPhones
Dim phone As CConstitPhone

phonesParent = CType(constit, IBBPhonesParent)
phones = phonesParent.Phones
phone = phones.Add()
phone.Fields(ECONSTITPHONEFIELDS.CONSTIT_PHONES_fld_PHONETYPE, "Home")
phone.Fields(ECONSTITPHONEFIELDS.CONSTIT_PHONES_fld_NUM, "123-4567")
phone.Fields(ECONSTITPHONEFIELDS.CONSTIT_PHONES_fld_IS_PRIMARY, True)

constit.Save()
constit.CloseDown()
constit = Nothing

Payment Type or Payment Method ID reference

Some time ago I wrote the mappings for gift type ids and their corresponding descriptions. This is useful in the API and also when interrogating the database directly. More recently I have needed to do the same thing with payment type. I find it very strange that in some scenarios this field is referred to payment type and other times it is payment or just pay method. Whichever you use the mappings are given below:

  Continue reading Payment Type or Payment Method ID reference

Checking access restrictions in The Raiser’s Edge

In a recent project I had to ensure that specific confidential information was being saved as an action. I had a custom screen where this data was going to be viewed and edited. Only certain users had access to that action using security by action types. I had to check to see if the current user was able to view the action and if they could whether or not they were able to edit the details. There are useful methods for this in the API and I assumed it would be a simple task to use them. Continue reading Checking access restrictions in The Raiser’s Edge

Batch API – error with tempRecords is nothing

“Nothing :
Init method must be called before using this object.” My colleague was testing a new application that I have written for a client. Every so often she would tell me that this error message was appearing in the control report. Everything else appeared to work. Just what did this mean? It was one of those classic RE error messages that means nothing. When I started to debug the problem I saw the error quite soon. Again it was one of those Batch API idiosyncrasies that just needed to be dealt with.

I was creating several gifts in a batch. I was looping through the gifts and creating each one.

For Each donation In donations
    tempRecords = CType(batch.TempRecords, Blackbaud.PIA.RE7.BatchData.CTempRecords)
    PopulateOneGift(constitSysId, batch, tempRecords.Add, donation)
    tempRecords.Save()
 Next

The error occurred on the second iteration of the loop. Whenever the tempRecords.Add
was called the error was raised. This was strange because normally you get this kind of error when you have not initialized the object or done a Closedown on it. Anyway the solution to add one line before calling the Add:

For Each donation In donations
    tempRecords = CType(batch.TempRecords, Blackbaud.PIA.RE7.BatchData.CTempRecords)
    tempRecords.Reload()
    PopulateOneGift(constitSysId, batch, tempRecords.Add, donation)
    tempRecords.Save()
 Next

That seemed to fix the problem.

EDIT: or so I thought. It appears as though it worked once then failed after that. I have now resorted to saving and closing the batch after each iteration and then opening it up again at the beginning. Ah the joys of the batch API.

Raiser’s Edge Integration with REST webservice

I have been working on a new version of RETweet which will be released soon. In that version we are allowing users to create constituents from Twitter users. The amount of information that you get from Twitter about the Tweet and the person that tweeted is very limited. The real name of the Twitter user – and it is not always a real name – is given in one field so it could include the first and last name (this is the most common where it is not a Tweeting business).  To make this more useful the application reaches out to a webservice to determine the gender of the person.

The webservice takes the first name and returns gender information and which countries the name appears in. The web service is biased towards European and Western first names but does claim to cover some countries in Asia too.

This could also easily be integrated as a VBA solution so that when you save and close a constituent record, if the gender is unknown the VBA code reaches out to the webservice and populates the code. Here is some sample code that shows you how this is done. This is done using .NET as it is considerably easier to achieve than in the native VBA environment. It would, however, be easy to make a call to a COM visible assembly from the native VBA client to call this method.

 

' Determines the gender of the person by looking up useing Thomas Bayer's webservice.
Private Sub SetGender(firstName As String, constit as CRecord) 

   Dim xdoc As XDocument
   Try
      'Here we retrieve the XML document from the REST web service
      xdoc = sendXML("http://www.thomas-bayer.com/restnames/name.groovy?name=" & firstName)

      Dim result = xdoc.<restnames>

      'if there is an error then the gender cannot be found
      If result.Descendants("error").Count > 0 Then
         Return
      End If

      'This is not an ideal check as a name can be both male and female
      'but for simplicity we assume this is not the case
      If resultresult.<nameinfo>.<male>.FirstOrDefault.Value.ToLower = "true" Then
         constit.Fields(ERECORDSFields.RECORDS_fld_GENDER) = "male"
      Else
         constit.Fields(ERECORDSFields.RECORDS_fld_GENDER) = "female"
      End If
      Catch
         'If there is an error due to the web service we catch it here and don't bother the end user.
      End Try

End Sub

Private Function sendXML(ByVal uri As String) As XDocument

   Dim request As HttpWebRequest
   Dim response As HttpWebResponse
   Dim reader As XmlReader

   request = HttpWebRequest.Create(uri)
   response = request.GetResponse()

   reader = XmlReader.Create(response.GetResponseStream())
   Return XDocument.Load(reader)

End Function

 

Look out for our new version of RETweet. It will include this and a lot of other new features!

A .NET Fix

This post is totally unrelated to The Raiser’s Edge and the API but in case others are having the same issue I wanted to describe how I fixed it. I have just spent the last several hours trying to get the progress bar to scroll in the marquee style. I had seen several posts referring to using a background worker to retrieve data from the database while the progress bar scrolled in the UI thread. It sounded like the right solution but it didn’t make a difference.

I read one post that helped. It said that before you instantiate your form you need to enable the visual styles.

Application.EnableVisualStyles()

When I ran my application all of a sudden not only was my progress bar scrolling nicely but all of a sudden I could see the graphical styles that I always had seen in Visual Studio but never experienced in my Winform applications.

The Problem with Quick Find

I have been working on a new product which will hopefully be released soon. In this new product the user needs to be able to look up a constituent in as few key strokes as possible. The approach that I took was to use the same functionality as the Quick Find mechanism that you see on the records page in The Raiser’s Edge. This is dependant on your user settings so that you can either put in a constituent id, the first name followed by the surname or the surname comma the first name. While I was testing it on the sample database it worked perfectly. However when I tested it on a client’s very large database there were issues. Continue reading The Problem with Quick Find

Looking up a constituent

Whenever I write a bespoke customisation for a client that needs to look up a constituent based on some biographical information I normally use the functionality available behind the scenes in IDLookup. If you are unfamiliar with IDLookup, it allows users to feed in an Excel or CSV file of names, addresses, aliases, attributes and all sorts of biographical information. Then based on criteria that you define, it will look to see if one or more constituents already exist in The Raiser’s Edge. I use much of the look-up functionality in other projects simply because Blackbaud chose not to make this functionality easily available. There is no back-end interface to their regular constituent look-up screen which is a real shame. The nearest feature is the IBBRecordFinder interface.

Continue reading Looking up a constituent