Create appointments (meetings) in SugarCRM via REST and perl

Subject

To prepare for some bigger project I plan with SugarCRM I decided to write a client program, which would create new appointments or meetings as they call it in SugarCRM.
Initially I was planning just to program a tool to create dummy data, but soon learned that creating an appointment is a bit more complicated task than for example creating a contact or an account.

Challenge

Digging through numerous examples, studying database structure and so on I soon realized that creating an appointment even for just one person, i.e. appointment owner, will require 3 following steps:
  1. Create ‘Meeting’ object
  2. Create ‘Meeting’ to ‘Contact’ relation
  3. Create ‘Meeting’ to ‘User’ relation

Now looking at the list above I have to correct myself. I’ll be creating appointments for 2 persons, one being an appointment owner and another one being an appointment contact. Think of it as you (appointment owner) are going to visit a customer (appointment contact).

Another challenge, which is worth to be mentioned is my personal addiction to perl as a scripting language. I especially like it for client-side development, but it’s not so often used in connection with SugarCRM, which is written in php. So, googling is of a limited help here looking for examples.

Platform/Tools

  • First of all, SugarCRM CE (Community Edition) version 6.5.16
  • ActiveState perl (v5.14.2) on Windows 7
  • And also one nice perl module Net::SugarCRM found on CPAN (version 2.17738).

Solution

Simple create appointment

Using Net::SugarCRM module this goes pretty straightforward:

use Net::SugarCRM;
use DateTime;
use Data::Random qw(:all);
...
my $s = Net::SugarCRM::Links->new(url=>$url, restuser=>$login, restpasswd=> $pass);
...
my $meeting_entry = {
       date_start => DateTime->now()->date() . ' ' .DateTime->now()->time(),
	   assigned_user_id => $user_id,
           duration_minutes => '60',
	   description => join(' ',rand_words( size => 5)),
	   name => join(' ',rand_words( )),
	   status => 'Planned',
	   parent_type => 'Contacts',
	   parent_id => $contact_id,
	   };

my $meeting_id = $s->create_module_entry('Meetings',$meeting_entry);

This will return the id of the new appointment, we just created. $user_id is the id of the appointment owner (myself) and $client_id is the id of the client, I’m going to meet.

Please also note the usage of DateTime and Data::Random to fill the content automatically with some random values.

It was very simple so far, but this is not all. Appointments are created, but not properly assigned. If we go to SugarCRM user interface, we won’t see these appointments in user’s calendar and also won’t see them in the list of activities for particular contacts. To solve this we will need to create relationships.

Extend Net::SugarCRM to handle relationships

Unfortunately Net::SugarCRM doesn’t offer a generic function to create relationships between objects (modules) in SugarCRM. Here I just decided to extend it via my own module Net::SugarCRM::Links having one additional method to support relationship creation.

package Net::SugarCRM::Links;
use parent Net::SugarCRM;
...
sub create_module_links {
    my ($self, $module, $module_id, $link_field_name, $related_ids, $attributes) = @_;

    foreach my $required_attr (@{$self->required_attr->{$module}}) {
        $self->log->logconfess("No $required_attr attribute. Not creating links in $module for: ".Dumper($attributes))
            if (!exists($$attributes{$required_attr}) ||
                !defined($$attributes{$required_attr}) ||
                $$attributes{$required_attr} eq '');
    }

    my $rest_data = '{"session": "'.$self->sessionid.'", "module_name": "'.$module
        . '", "module_id": "' . $module_id . '", "link_field_name": "'
        . $link_field_name. '", "related_ids": ' . encode_json($related_ids)
        . ', "name_value_list": '. encode_json($attributes). '}';

    my $response = $self->_rest_request('set_relationship', $rest_data);
    $self->log->info( "Successfully created link in <".encode_json($attributes)."> with sessionid ".$self->sessionid."\n");
    $self->log->debug("Link created in module $module was:".Dumper($response));
    #return $response->{id};
	return $response;
}

The above code is almost “Copy&Paste” from the parent’s method create_module_entry with very few changes.

Line 14 creates a different REST request string and in the call _rest_reqeust in line 19 we use set_relationship from SugarCRM REST API.

Please note that I kept the code to check for required attributes (line 7), but it has no practical meaning. To get any use of it one would need to add additional modules and attributes in the parent’s class required_attr array. This exercise is left to the reader.

Add links to Contact and User

There are two many-to-many relationships we would need to create using the above Net::SugarCRM::Links module. Nothing too special with that,  just review the code below.

use Net::SugarCRM::Links;

my $link_entry = {
		module => 'Meetings',
		meeting_id => $meeting_id,
		module => 'Contacts',
		contact_id => $contact_list[$i]->{'id'}
	};

my $out = $s->create_module_links('Meetings',$meeting_id,'contacts',[$contact_id],$link_entry);

my $user_link_entry = {
	meeting_id => $meeting_id,
	user_id => $user_id,
	accept_status => 'accept',
	required => 1
};

$out = $s->create_module_links('Meetings',$meeting_id,'users',[$user_id],$user_link_entry);

I intentionally don’t include error handling here to keep is simple, basically you are advised to traverse through the output of the create_module_links method, this works as documented in the API documentation. In particular you will see how many relationships are created, deleted and failed.

Please observe the 3-d argument to the method create_module_links above in lines 10 and 19. This is what the API documentation describes as

link_field_name -- name of the link field which relates to the other module for which the relationship needs to be generated.

And this is a nice pitfall. My first understanding was that this should be something like ‘meetings_contacts’ as a field name and it took a while before I realized that this should be just a module name to which we are creating our relationships.

Discussion

Not much to discuss here, so it is time for some screenshots.
Going through the main menu Activities->View Meeting will show something like the below:
Activitie->Meetings->View Meetings
Activitie->Meetings->View Meetings
Below are created appointments in the calendar month view:
Calendar
Calendar

And finally when we go to a particular contact, we see all these meeting listed in the Activities section:

Contact Activities
Contact Activities

Caveats

Just a word of caution – I don’t pretend to provide a complete solution, describe any best practice or a like. I intentionally don’t provide complete source code listing because it is not a production quality code, not at all.
 
Now to the real problem I discovered when reviewing this article. I was under the impression that with the code above I also set meeting to accepted for the meeting owner. See line 15 in the above code snipped. This turned out to be wrong, it doesn’t work  as I expected and in my next article I’ll describe the solution.
As an illustration for the above problem see the screenshot below, which is My Meetings Dashlet.
My Meeting Dashlet
My Meeting Dashlet
Compare row 1 and 2. In the first row meeting is accepted by the owner and in the second row accept_status update didn’t work and the meeting owner could change the status directly in UI.
Solution found, so stay tuned!

 

Comments

comments

Leave a Reply