2

I have a custom fieldset on Contributions that uses an option list. The option list has a default value set, but when contributions are created by non-UI processes (e.g. API) they do not have this default value; the field is empty/null/not there.

The fieldset is created by an extension. How could I ensure the default is set for all contributions?

Seems the options might be:

  • Use hook_civicrm_post then use the API to make a call.
  • Or could hook_civicrm_pre be used for this? Can you set custom fields in this part of the process?
  • or a horrible way would be to write an API job to somehow create default entries and run this by cron. Perhaps this might be necessary to set a default for existing contributions though?

Anyone done this? If so, how d'ya do it?

3

Here's how I've opted to do it. Please do submit alternative answers if there's a better way.

Use the civicrm_post hook like this:

Note that you need to replace N with your custom field's ID number

function myext_civicrm_post($op, $objectName, $objectId, &$objectRef) {

  if ($objectId && $op == 'create' && $objectName == 'Contribution') {

    // See if we have a field that belongs to the fieldset.
    $result = civicrm_api3('CustomValue', 'get', [
      'sequential' => 1,
      'entity_id' => $objectId,
      'entity_type' => "Contribution",
      'return.custom_N' => 1,
    ]);

    if ($result['count'] == 0) {
      // Create the default entry.
      $result = civicrm_api3('CustomValue', 'create', [
        'sequential' => 1,
        'entity_id' => $contribution_id,
        'entity_type' => "Contribution",
        'custom_N' => 'unknown',
        // custom_M ...
      ]);
    }

  }
}

Then when you create a Contribution, the defaults are properly set.

Interesting observations

My interest was in creating the default record when the contribution was created by the API. But I thought I'd look into how this works with using the UI too.

If you use the web UI to create a contribution then the code above gets in before the main process creates/sets the custom values. i.e. This is the order of things:

  1. User creates a contribution record.
  2. Contribution record created.
  3. Our hook runs, no custom value exists so it creates the default record.
  4. The user-entered values (inc. the defaults that were set up in the form) overwrite the default created by our hook.

The same process happens when you use the API to create a contribution but include values for the custom data also.

1

Based on the answer of @artfulrobot (thanks!), i have made a version of the code that use the definition of the custom field itself. If we have a default value, then force the creation of the field, otherwise, just ignore. Of course, it's probably a bit slower because we do some extra api calls, but it means we could change the defaults in the UI without changing the code.


function myext_civicrm_post($op, $objectName, $objectId, &$objectRef) {

  if ($objectName == 'Contribution') {

    $result = civicrm_api3('CustomField', 'get', [
      'sequential' => 1,
      'return' => ["default_value", "custom_group_id", "name"],
      'default_value' => ['IS NOT NULL' => 1],
      // use your own custom group id
      'custom_group_id.id' => 1,
    ]); 
    if ($result['count']) {
      // use the first one (any will do because we know that we are in one custom group)
      $check_field = 'custom_' . $result['values'][0]['id'];

      // See if we have a field that belongs to the fieldset.
      $existResult = civicrm_api3('CustomValue', 'get', [
        'sequential' => 1,
        'entity_id' => $objectId,
        'entity_type' => "Contribution",
        'return.' . $check_field => 1,
      ]); 

      if ($existResult['count'] == 0) {
        // insert a new entry with defaults values
        $p = [ 
          'sequential' => 1,
          'entity_id' => $objectId,
          'entity_type' => 'Contribution'
        ];  
        foreach ($result['values'] as $row) {
          $id = $row['id'];
          $p['custom_' . $id] = $row['default_value'];
        }   

        // Create the default entry.                                                                                                        
        $result = civicrm_api3('CustomValue', 'create', $p);
      }   
    }

  }

}

I guess with a bit of extra effort, one could make a generic extension that works for any custom fields that attach to any entity type.

0

I've handled this using hook_civicrm_custom. This fires after the db write, which isn't ideal, but has a much simpler set of params than hook_civicrm_pre.

  • Does this call when Contributions are created? My db is not showing any record being written to the custom data table, which I think would imply that this hook would not fire (because the docs say "This hook is called AFTER the db write on a custom table" and no write has happened)? – artfulrobot Jan 5 '17 at 17:49
  • Ah - I guess in my instance, I had other custom fields that were being written. I don't think my solution will apply to your use case. – Jon G - Megaphone Tech Jan 5 '17 at 17:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.