{"id":212,"date":"2013-10-31T00:46:16","date_gmt":"2013-10-30T20:46:16","guid":{"rendered":"http:\/\/www.bandidor.info\/wp\/?p=212"},"modified":"2016-02-02T12:50:40","modified_gmt":"2016-02-02T09:50:40","slug":"create-appointments-meetings-in-sugarcrm-via-rest-and-perl","status":"publish","type":"post","link":"https:\/\/www.bandidor.info\/wp\/?p=212","title":{"rendered":"Create appointments (meetings) in SugarCRM via REST and perl"},"content":{"rendered":"<h1><strong>Subject<\/strong><\/h1>\n<div>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.<\/div>\n<div>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.<\/div>\n<h1><strong>Challenge<\/strong><\/h1>\n<div>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:<\/div>\n<ol>\n<li>Create &#8216;Meeting&#8217; object<\/li>\n<li>Create &#8216;Meeting&#8217; to &#8216;Contact&#8217; relation<\/li>\n<li>Create &#8216;Meeting&#8217; to &#8216;User&#8217; relation<\/li>\n<\/ol>\n<p>Now looking at the list above I have to correct myself. I&#8217;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).<\/p>\n<div>Another challenge, which is worth to be mentioned is my personal addiction to <code>perl<\/code> as a scripting language. I especially like it for client-side development, but it&#8217;s not so often used in connection with SugarCRM, which is written in <code>php<\/code>. So, googling is of a limited help here looking for examples.<\/div>\n<div><\/div>\n<h1><span style=\"color: #000000;\"><b>Platform<\/b><\/span><strong>\/Tools<\/strong><\/h1>\n<div>\n<ul>\n<li><span style=\"color: #444444; font-family: Georgia, 'Bitstream Charter', serif; line-height: 1.5;\">First of all, SugarCRM CE (Community Edition) version\u00a06.5.16<\/span><\/li>\n<li>ActiveState perl\u00a0(v5.14.2) on Windows 7<\/li>\n<li>And also one nice perl module <a href=\"http:\/\/search.cpan.org\/~nito\/Net-SugarCRM\/lib\/Net\/SugarCRM.pm\" target=\"_blank\">Net::SugarCRM<\/a> found on CPAN (version\u00a02.17738).<\/li>\n<\/ul>\n<\/div>\n<h1><strong>Solution<\/strong><\/h1>\n<h2>Simple create appointment<\/h2>\n<p>Using <code>Net::SugarCRM<\/code>\u00a0module this goes pretty straightforward:<\/p>\n<pre class=\"brush: perl; title: ; notranslate\" title=\"\">\r\nuse Net::SugarCRM;\r\nuse DateTime;\r\nuse Data::Random qw(:all);\r\n...\r\nmy $s = Net::SugarCRM::Links-&gt;new(url=&gt;$url, restuser=&gt;$login, restpasswd=&gt; $pass);\r\n...\r\nmy $meeting_entry = {\r\n       date_start =&gt; DateTime-&gt;now()-&gt;date() . ' ' .DateTime-&gt;now()-&gt;time(),\r\n\t   assigned_user_id =&gt; $user_id,\r\n           duration_minutes =&gt; '60',\r\n\t   description =&gt; join(' ',rand_words( size =&gt; 5)),\r\n\t   name =&gt; join(' ',rand_words( )),\r\n\t   status =&gt; 'Planned',\r\n\t   parent_type =&gt; 'Contacts',\r\n\t   parent_id =&gt; $contact_id,\r\n\t   };\r\n\r\nmy $meeting_id = $s-&gt;create_module_entry('Meetings',$meeting_entry);\r\n<\/pre>\n<p>This will return the id of the new appointment, we just created. <code>$user_id<\/code>\u00a0is the id of the appointment owner (myself) and <code>$client_id<\/code>\u00a0is the id of the client, I&#8217;m going to meet.<\/p>\n<p>Please also note the usage of <code>DateTime<\/code>\u00a0and<code> Data::Random<\/code> to fill the content automatically with some random values.<\/p>\n<p>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&#8217;t see these appointments in user&#8217;s calendar and also won&#8217;t see them in the list of activities for particular contacts. To solve this we will need to create relationships.<\/p>\n<h2>Extend Net::SugarCRM to handle relationships<\/h2>\n<p>Unfortunately <code>Net::SugarCRM<\/code>\u00a0doesn&#8217;t offer a generic function to create relationships between objects (modules) in SugarCRM. Here I just decided to extend it via my own module\u00a0<code>Net::SugarCRM::Links<\/code>\u00a0having one additional method to support relationship creation.<\/p>\n<pre class=\"brush: perl; highlight: [7,14,19]; light: false; title: ; notranslate\" title=\"\">\r\npackage Net::SugarCRM::Links;\r\nuse parent Net::SugarCRM;\r\n...\r\nsub create_module_links {\r\n    my ($self, $module, $module_id, $link_field_name, $related_ids, $attributes) = @_;\r\n\r\n    foreach my $required_attr (@{$self-&gt;required_attr-&gt;{$module}}) {\r\n        $self-&gt;log-&gt;logconfess(&quot;No $required_attr attribute. Not creating links in $module for: &quot;.Dumper($attributes))\r\n            if (!exists($$attributes{$required_attr}) ||\r\n                !defined($$attributes{$required_attr}) ||\r\n                $$attributes{$required_attr} eq '');\r\n    }\r\n\r\n    my $rest_data = '{&quot;session&quot;: &quot;'.$self-&gt;sessionid.'&quot;, &quot;module_name&quot;: &quot;'.$module\r\n        . '&quot;, &quot;module_id&quot;: &quot;' . $module_id . '&quot;, &quot;link_field_name&quot;: &quot;'\r\n        . $link_field_name. '&quot;, &quot;related_ids&quot;: ' . encode_json($related_ids)\r\n        . ', &quot;name_value_list&quot;: '. encode_json($attributes). '}';\r\n\r\n    my $response = $self-&gt;_rest_request('set_relationship', $rest_data);\r\n    $self-&gt;log-&gt;info( &quot;Successfully created link in &lt;&quot;.encode_json($attributes).&quot;&gt; with sessionid &quot;.$self-&gt;sessionid.&quot;\\n&quot;);\r\n    $self-&gt;log-&gt;debug(&quot;Link created in module $module was:&quot;.Dumper($response));\r\n    #return $response-&gt;{id};\r\n\treturn $response;\r\n}\r\n<\/pre>\n<p>The above code is almost &#8220;Copy&amp;Paste&#8221; from the parent&#8217;s method <code>create_module_entry<\/code>\u00a0with very few changes.<\/p>\n<p>Line 14 creates a different REST request string and in the <code>call _rest_reqeust<\/code>\u00a0in line 19 we use\u00a0<code>set_relationship<\/code>\u00a0from SugarCRM REST API.<\/p>\n<p>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&#8217;s class\u00a0<code>required_attr<\/code>\u00a0array. This exercise is left to the reader.<\/p>\n<h2>Add links to Contact and User<\/h2>\n<p>There are two many-to-many relationships we would need to create using the above <code>Net::SugarCRM::Links<\/code>\u00a0module. Nothing too special with that, \u00a0just review the code below.<\/p>\n<pre class=\"brush: perl; highlight: [10,19]; light: false; title: ; notranslate\" title=\"\">\r\nuse Net::SugarCRM::Links;\r\n\r\nmy $link_entry = {\r\n\t\tmodule =&gt; 'Meetings',\r\n\t\tmeeting_id =&gt; $meeting_id,\r\n\t\tmodule =&gt; 'Contacts',\r\n\t\tcontact_id =&gt; $contact_list&#x5B;$i]-&gt;{'id'}\r\n\t};\r\n\r\nmy $out = $s-&gt;create_module_links('Meetings',$meeting_id,'contacts',&#x5B;$contact_id],$link_entry);\r\n\r\nmy $user_link_entry = {\r\n\tmeeting_id =&gt; $meeting_id,\r\n\tuser_id =&gt; $user_id,\r\n\taccept_status =&gt; 'accept',\r\n\trequired =&gt; 1\r\n};\r\n\r\n$out = $s-&gt;create_module_links('Meetings',$meeting_id,'users',&#x5B;$user_id],$user_link_entry);\r\n<\/pre>\n<p>I intentionally don&#8217;t include error handling here to keep is simple, basically you are advised to traverse through the output of the <code>create_module_links<\/code>\u00a0method, this works as documented in the API documentation. In particular you will see how many relationships are created, deleted and failed.<\/p>\n<p>Please observe the 3-d argument to the method\u00a0<code>create_module_links<\/code>\u00a0above in lines 10 and 19. This is what the API documentation describes as<\/p>\n<p><code>link_field_name -- name of the link field which relates to the other module for which the relationship needs to be generated.<\/code><\/p>\n<p>And this is a nice <strong>pitfall<\/strong>. My first understanding was that this should be something like &#8216;meetings_contacts&#8217; 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.<\/p>\n<h1><strong>Discussion<\/strong><\/h1>\n<div>Not much to discuss here, so it is time for some screenshots.<\/div>\n<div>Going through the main menu <em>Activities<\/em>-&gt;<em>View Meeting<\/em> will show something like the below:<\/div>\n<div>\n<figure id=\"attachment_249\" aria-describedby=\"caption-attachment-249\" style=\"width: 300px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot047.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-249\" src=\"http:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot047-300x125.png\" alt=\"Activitie-&gt;Meetings-&gt;View Meetings\" width=\"300\" height=\"125\" srcset=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot047-300x125.png 300w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot047-250x104.png 250w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot047-150x62.png 150w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot047.png 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-249\" class=\"wp-caption-text\">Activitie-&gt;Meetings-&gt;View Meetings<\/figcaption><\/figure>\n<\/div>\n<div>Below are created appointments in the calendar month view:<\/div>\n<div>\n<figure id=\"attachment_251\" aria-describedby=\"caption-attachment-251\" style=\"width: 300px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot048.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-251\" src=\"http:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot048-300x266.png\" alt=\"Calendar\" width=\"300\" height=\"266\" srcset=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot048-300x266.png 300w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot048-169x150.png 169w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot048-150x133.png 150w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot048.png 541w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-251\" class=\"wp-caption-text\">Calendar<\/figcaption><\/figure>\n<\/div>\n<p>And finally when we go to a particular contact, we see all these meeting listed in the Activities section:<\/p>\n<figure id=\"attachment_250\" aria-describedby=\"caption-attachment-250\" style=\"width: 300px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot049.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-250\" src=\"http:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot049-300x182.png\" alt=\"Contact Activities\" width=\"300\" height=\"182\" srcset=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot049-300x182.png 300w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot049-246x150.png 246w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot049-150x91.png 150w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot049.png 640w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><figcaption id=\"caption-attachment-250\" class=\"wp-caption-text\">Contact Activities<\/figcaption><\/figure>\n<h1><strong>Caveats<\/strong><\/h1>\n<div>Just a word of caution &#8211; I don&#8217;t pretend to provide a complete solution, describe any best practice or a like. I intentionally don&#8217;t provide complete source code listing because it is not a production quality code, not at all.<\/div>\n<div><strong>\u00a0<\/strong><\/div>\n<div>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 <em>accepted<\/em> for the meeting owner. See line 15 in the above code snipped. This turned out to be wrong, it doesn&#8217;t work \u00a0as I expected and in my next article I&#8217;ll describe the solution.<\/div>\n<div>As an illustration for the above problem see the screenshot below, which is My Meetings Dashlet.<\/div>\n<div>\n<figure id=\"attachment_257\" aria-describedby=\"caption-attachment-257\" style=\"width: 639px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot050.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-257\" src=\"http:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot050.png\" alt=\"My Meeting Dashlet\" width=\"639\" height=\"234\" srcset=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot050.png 639w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot050-300x109.png 300w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot050-250x91.png 250w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2013\/10\/ScreenShot050-150x54.png 150w\" sizes=\"auto, (max-width: 639px) 100vw, 639px\" \/><\/a><figcaption id=\"caption-attachment-257\" class=\"wp-caption-text\">My Meeting Dashlet<\/figcaption><\/figure>\n<\/div>\n<div>Compare row 1 and 2. In the first row meeting is accepted by the owner and in the second row <code>accept_status <\/code>update didn&#8217;t work and the meeting owner could change the status directly in UI.<\/div>\n<div><\/div>\n<div><strong>Solution found, so stay tuned!<\/strong><\/div>\n<div>\n<p><strong>\u00a0<\/strong><\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>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&#8230;<\/p>\n","protected":false},"author":1,"featured_media":262,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[12],"tags":[14,15,25],"class_list":["post-212","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-sugarcrm","tag-perl","tag-rest","tag-sugarcrm"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/ngg_featured\/dsci0008.jpg","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p2EszU-3q","_links":{"self":[{"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/posts\/212","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=212"}],"version-history":[{"count":39,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/posts\/212\/revisions"}],"predecessor-version":[{"id":600,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/posts\/212\/revisions\/600"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/media\/262"}],"wp:attachment":[{"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=212"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=212"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=212"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}