Sunday, September 20, 2015

The iOS 9 upgrade bug... and fix!



Dear iPad & iOS users, this week my wife came across the "iOS 9 bug update" and was afraid to loose all her data. Her iPad asked if she would be keen on upgrading to iOS 9. She was not aware of the potential issues and did not make a backup before. Why should we, when the iPad suggests the upgrade in such an informal way? This should not be rocket science.

Here is the fix I was able to perform. I wrote this blog post, because I did not find the solution on the Internet and like to share it.


The issue

After upgrading to iOS 9, the iPad rebooted, showed a slide to continue, and then froze. There was no way to boot the device correctly. Every reboot led to this same slide and froze. Even more, iTunes refused to make a backup or to install again iOS 9.


The solutions available on the Internet

None of them worked for me. E.g. http://www.techtimes.com/articles/86342/20150919/apple-addresses-ios-9-slide-to-upgrade-issue-heres-what-to-do.htm


My fix

Here the walkthrough, it is really easy.

1) disable the location feature on iCloud.com for the device.

2) Download the iOS 8 update for your device
http://osxdaily.com/2015/08/13/ios-8-4-1-update-ipsw-download/
Expect to download a 2Gb file.

3) iTunes
Connect the iPad to your laptop.
When it is recognized on iTunes, click on the "Update" button pressing the Alt key simultaneously (this key is also called the option key). iTunes will open a window and let you select the OSX update file.
The update file is designed to update from a lower OSX version; actually we are making a downgrade, but it worked like a charm for me.

After a device reboot, the iPad asked for my wifi credentials, then my wife's Apple ID and everything was hopefully back and working.


Do backup!

My biggest takeaway is that regular backups are making sense and can save your life (or at least your time). 

Although, a 32Gb backup is too big for my laptop, I chose to store the backup on an external hard drive. 
I do not know an easy way to change the path on iTunes, so I moved the backup directory to an external drive (~/Library/Application Support/MobileSync/Backup) and I created a symbolic link to the HD using a shell command. In my case:
cd /Users/olivier/Library/Application Support/MobileSync
Then
ln -s /Volumes/HD_backup/Backups_mobile ./Backup
Where HD_backup is your external HD name and Backups_mobile the destination directory.

Enjoy your iPad, and iOS 8 ;-)

Tuesday, December 23, 2014

A Bug's Life or why I joined SugarCRM 3 years ago

It is time for a new post! I was extremely busy with my job this year and did not take the time to update that blog. There will be so much to say: Sugar 7 is an amazing product... I will save some time in the coming weeks to post useful contents to help to leverage your daily practice of Sugar 7. 

However, this post will not be related to Sugar 7. Yesterday I watched "A Bug's Life" with my kids. A scene at the beginning of the movie really blew my mind. I realized this was the perfect illustration of why I left Salesforce for SugarCRM 3 years ago.

Here is the scene (Copyright Pixar Animation Studios):



See on YouTube

And the script:

Flik: This is my new idea for harvesting grain. No more picking individual kernels. You cut down the entire stalk!
Princess Atta: We don't have time for this.
Flik: Exactly! We never have time to collect food for ourselves. We spend all summer harvesting for the offering. But my invention will speed up production.
[...]
Chief Ant #1: Listen! The princess doesn't have time for this! You wanna help fill this thing? Get rid of that machine, get back in line and pick grain like everybody else!
Chief Ant #2: Like everybody else!
Princess Atta: Please, Flik. Just go.
Flik: I'm sorry, I was really just trying to help.
Chief Ant #2: Harvester. Why, we harvested the same way ever since I was a pupa.


This is so representative of my previous position at Salesforce. No room for innovation, for the individual's thought. Each employee was like an ant dedicated to a specific task, and do not dare trying to go beyond your responsibilities, you would be blame for this.

Joining SugarCRM was definitely a career booster: I am doing pre-Sales for the Emea region, CRM evangelisation, public presentations, developer trainings and now I am helping writing parts of the product! Working with Sugar 7 is such a trill. Based on my 7 years experience in the CRM industry, this is the best CRM solution in terms of configuration, enhancement, coding and integration, you will never get bored or blocked by any limit. But this is another story.

I wish all of you a Merry Christmas!
Take care.

Thursday, February 20, 2014

Futur datacenter en France, ce que Salesforce ne vous dit pas !

Salesforce vient d’annoncer sa décision de monter un data center en France. Une photo de groupe prise en Californie réunissant François Hollande et Marc Benioff, une promotion sur les réseaux sociaux par Fleur Pellerin, il n’en fallait pas moins pour que l’information circule durant deux jours sur tous les médias IT francophones. Restons calme, respirons un bon coup et prenons du recul.

Pourquoi un data center en France ?
Les data centers Salesforce appelés EMEA (acronyme pour Europe, Moyen Orient et Afrique) ne sont pas situés en Europe mais… aux États-Unis ! Géographiquement, c'est un peu loin pour les clients qui ont une forte intégration entre Salesforce et leurs applications locales, voire même une utilisation standard de l'outil. 

Déjà un data center en Angleterre prévu en 2014
C’est pour cela que Salesforce a décidé d’ouvrir un data center en Angleterre. La nouvelle fut bien accueillie, mais elle ne satisfaisait toujours pas les sociétés qui sont légalement obligées d’héberger leurs données sur le territoire français. C’est un pré requis partagé par l’industrie médicale qui stocke les données des patients ainsi que certaines sociétés qui manipulent des données financières sensibles. Mais c’est également un point d’inquiétude pour les sociétés qui s’inquiètent du Patriot Act. Rappelons que le Patriot Act autorise le gouvernement américain à disposer de toutes les informations hébergées sur le sol américain ou ailleurs du moment que l'opérateur est américain, sans obligation de notifier la société victime de cet accès. L’affaire Snowden a révélé à quel point la NSA pouvait être gourmande sur le sujet.

Donc un data center en France pour 2015...
Devons nous sauter de joie ? Les données médicales enfin gérées dans Salesforce ? C’est sans compter sur un nouvel invité surprise : le Disaster Recovery, c’est-à-dire la gestion d’un désastre s’abattant sur le data center : panne de courant persistante, cataclysme naturel (tremblement de terre, inondation), Godzilla, etc. Pour se prémunir d’une interruption de service durable, le data center est répliqué en temps quasi réel vers un site de secours. Salesforce peut alors basculer ses services vers le nouveau site. 

…Mais répliqué aux USA !
Et maintenant, la question à un cent : où va se situer le site de Disaster Recovery ? A votre avis ? Très loin du data center, c'est logique, et certainement dans l’infrastructure existante, là ou Salesforce gère déjà son Disaster Recovery. Ce qui laisse présager que le Disaster Recovery se situera sur le sol américain, les données tombant directement sous le coup du Patriot Act, again...
Et si la réplication se faisait en Angleterre, le résultat ne serait pas différent.

En conclusion
La création de ce data center est certes une preuve de l’investissement de Salesforce sur le marché français mais n’apportera pas de réponse aux contraintes juridiques des sociétés médicales et bancaires.


Références
- Le Monde : Opération séduction de M. Hollande dans la Silicon Valley
- Situation géographique des data center Salesforce, sites de production et backup : https://success.salesforce.com/ideaView?id=087300000006ntCAAQ 
- Le tweet de Fleur Pellerin : https://twitter.com/fleurpellerin/status/435382415193018368


Saturday, May 25, 2013

Batch synchronization between your system and Sugar

Summary
This article presents how to write and set up a batch script that synchronizes data from your system with the data in Sugar.


The use case
You want to synchronize back office data from SAP, Sage or any other system with your CRM. But the original system is a closed software so it is uneasy to write a connector with Sugar.

The solution
A scheduled batch is exporting the updated data from the external system in a CSV file and then a PHP script is making the synchronization with Sugar using the API.

The external system will be the master of the data as this example is a one-way synchronization.


How it works
The synchronization is based on an external key from the master system. If the key is found in Sugar the record is updated, otherwise it is created.

The synchronization will be performed with a PHP script that will be scheduled and run on your server. It will read a CSV file and call the Sugar API. Your Sugar instance might be on site or in any cloud; the only requirement is that the script can reach it (ports: HTTP call 80 or 443 / TCP).

The CSV header (first row) have to be the technical names in Sugar. The external key might be in any column.

All the actions are stored in a human readable log file that is formatted to let you build an automatic process to analyze the results.


Understanding how Sugar is handling bad data
This is important that you fully understand how Sugar is handling bad data.
E.g. a wrong formatted date, a bad record id reference (the account id related to a contact), a bad data type (string instead of number), a wrong field name: Sugar will ignore silently the error and set the field to a null or an empty value. It will not raise an error.


Understanding the script
define('VERBOSE'true);
The script is writing a log file called logs.txt. Set VERBOSE to true will write the log to the output as well.

Configuration
The instance url:
$url 'http://yourdomain.sugarcrm.eu/service/v2/rest.php';
The external id field name:
$extidname 'externalid_c';

The data file name:

$import_file './test_data.csv';


1) Log in
Log in your instance. You must log in with an admin user; otherwise, if you have a private model you are taking the risk that the interface user will not see all the records and fails matching external ids and create duplicated records instead updating records.

2) read the csv file to get all the external ids
The external ids will be stored in the array $extid.

Make a get_entry_list call with the following parameters:
    $parameters = array(
        
'session' => $sessionid,
        
'module_name' => 'Accounts',
        
'query' => "$extidname IN ($extidlst)",
        
'name',
        
'offset' => 0,
        
'select_fields' => array('id', $extidname),
        
'link_name_to_fields_array' => array(),
        
'max_results' => '1000',
        
'deleted' => '0'
    
);

Change the module_name to your target module.

3) import the data
Read the data file and build an array of records. If the external id exists in Sugar then the record will be updated (we retrieve the Sugar id), otherwise it will be created. This is a kind of upsert (see on wikipedia).

Remark
If you run by mistake the script twice, the data quality will not be compromised because the second time all the records will be updated with the same data.

Log file
The log file is split into 2 sections.
The section called "search ext id" shows the data analysis: which record will be inserted or updated.
Then the script is calling the api method set_entries and fills the section "import data" with the result of the upsert.


In the example, there are 2 records in the CSV file. The first one had been updated, the second inserted.


2013-05-24 23:55:34,CMT,###START NEW LOG###
2013-05-24 23:55:39,CMT,## search ext id
2013-05-24 23:55:39,UPDATE,abc124,b5984f13-d762-9708-8f19-519f096d38f4
2013-05-24 23:55:39,INSERT,abc125,Acme 15
2013-05-24 23:55:39,CMT,## import data
2013-05-24 23:55:40,UPDATED,abc124,b5984f13-d762-9708-8f19-519f096d38f4
2013-05-24 23:55:40,INSERTED,abc125,8b236d6a-981d-803b-c83a-519fe1746037

The data file

"externalid_c","name","phone_office","phone_fax","email1","website","billing_address_street","billing_address_postalcode","billing_address_city","billing_address_country"
"abc124","Acme 14","05 55 11 22 32","05 55 11 22 34","contact@acme14.com","www.acme14.com","41 RUE WINSTON CHURCHILL",87000,"LIMOGES","FRANCE"
"abc125","Acme 15","01 40 55 66 77","01 40 55 66 78","contact@acme15.org","www.acme15.org","6 PLACE DE LA MADELEINE",75008,"PARIS","FRANCE"


The complete source code

Download the code on Pastebin http://pastebin.com/RdXYAaQn

<?php
ini_set
("auto_detect_line_endings"true);

define('VERBOSE'true);

function 
addlog($h$s) {
    
$s=date('Y-m-d H:i:s').",$s\n";
    if (
VERBOSE) echo $s;
    
fputs($h$s);
}


//
// 1) LOGIN
//
$handlew = @fopen('logs.txt'"a");
if (!
$handlew) die("cannot write to the log file\n");

addlog($handlew,'CMT,###START NEW LOG###');
$url 'http://yourdomain.sugarcrm.eu/service/v2/rest.php';
$curl curl_init($url); 
curl_setopt($curlCURLOPT_POSTtrue); 
curl_setopt($curlCURLOPT_HEADERfalse); 
curl_setopt($curlCURLOPT_RETURNTRANSFERtrue); 
$parameters =
  array(
    
"user_auth" =>
        array(
        
'user_name' => "will",
        
'password' => md5('will'),
        ),
    
"application_name" => ''
  
);

$json json_encode($parameters); 
$postArgs 'method=login&input_type=json&response_type=json&rest_data=' $json;
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs); 
$response curl_exec($curl); 
$result json_decode($response);
if(!
is_object($result)) { die("Connection error\n"); }
if (isset(
$result->number)) { // Invalid Login
    
addlog($handlew,'ERROR,'.$result->name);
    
fclose($handlew);
    exit;
}
$sessionid $result->id;

$userid=$result->name_value_list->user_id->value;


//
// 2) read CSV - get the external Id
//

$extidname 'externalid_c';
$import_file './test_data.csv';
$separator ',';
$handle = @fopen($import_file"r");
if (!
$handle) die("cannot open the import file $import_file\n");

$extid = array();
$headers1 fgetcsv($handle1000","'"');
$headers2 = array();
for(
$i=0;$i<count($headers1);$i++) $headers2[$headers1[$i]] = $i;
while ((
$a fgetcsv($handle1000","'"')) !== false) {
    if (
count($a) <= 1) continue;
    
array_push($extid"'".$a[$headers2[$extidname]]."'");
}
if (!
feof($handle)) die("Error: unexpected fgets() fail\n");

fclose($handle);

//
// 3) Mapping: select accounts based on the external Id
//

$extidlst implode(","$extid);
if (
$extidlst!="''") {
    
$extidlst implode(","$extid);
    
$parameters = array(
        
'session' => $sessionid,
        
'module_name' => 'Accounts',
        
'query' => "$extidname IN ($extidlst)",
        
'name',
        
'offset' => 0,
        
'select_fields' => array('id'$extidname),
        
'link_name_to_fields_array' => array(),
        
'max_results' => '1000',
        
'deleted' => '0'
    
);
    
$json json_encode($parameters);
    
$postArgs 'method=get_entry_list&input_type=json&response_type=json&rest_data=' $json;
    
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs);
    
$response curl_exec($curl);
    
$result json_decode($response);
    
$externalid2sg = array();
    foreach (
$result->entry_list as $i)
        
$externalid2sg[$i->name_value_list->$extidname->value] = $i->id;
}


//
// 4) Import
//

addlog($handlew,'CMT,## search ext id');
$handle = @fopen($import_file"r");
if (!
$handle) die('cannot open the import file');

$headers = array();
$records = array();
$logs = array();

while ((
$a fgetcsv($handle1000","'"')) !== false) {
    if (
count($a) <= 1) continue;
    if (
count($headers)==0) {
        
$headers $a;
        continue;
    }
    
$record = array();
    
$extid=$a[$headers2[$extidname]];
    for(
$i=0$i<count($a); $i++) {
        if (
$headers[$i] == "NA") continue; // NA for a column you want to skip
        
$field = array();
        
$field['name'] = $headers[$i];
        
$field['value'] = $a[$i];
        
array_push($record$field);
    }
    
// case update
    
if (isset($externalid2sg[$extid])) {
        
$field = array();
        
$field['name'] = 'id';
        
$field['value'] = $externalid2sg[$extid];
        
array_push($record$field);
        
addlog($handlew,"UPDATE,$extid,".$externalid2sg[$extid]);
        
array_push($logs"UPDATED,$extid,");
    } else {
        
addlog($handlew,"INSERT,$extid,".$a[$headers2['name']]);
        
array_push($logs"INSERTED,$extid,");
    }
      
array_push($records$record);
}
if (!
feof($handle)) die("Error: unexpected fgets() fail\n");

fclose($handle);addlog($handlew,'CMT,## import data');
$parameters = array(
    
'session' => $sessionid,
    
'module_name' => 'Accounts',
    
'name_value_lists' => $records   );

$json json_encode($parameters);
$postArgs 'method=set_entries&input_type=json&response_type=json&rest_data=' $json;
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs);
$response curl_exec($curl);
$result json_decode($response);
$j 0;
foreach (
$result->ids as $id)
    
addlog($handlew,$logs[$j++].$id);

curl_close($curl); 
fclose($handlew);
?>

Tuesday, May 21, 2013

Concurrent update with Sugar

Today I would like to share with you a useful feature of Sugar that is not well known: the lock record when two users are updating the same record.

Let's assume that Will is editing the opportunity "ABC opty 100 units" in Sugar. He wants to update the amount. At the same time, Jim is editing this opportunity. Will is faster than Jim and finished the editing before Jim and saved the opportunity. When Jim is clicking on the save button, he is firing the Sugar optimistic Lock. Sugar is showing a special page that is asking to Jim if he wants to commit his modification or not. Jim can see exactly the differences:


If the change had not been made by Will but by an interface (API user) the result would have been exactly the same for Jim.

This feature is very useful and prevent the concurrent update of the same record by two users.

Blog is now fixed!

Hi readers,

these last days this blog was awfully slow due to a bad DNS configuration on my personal web site. External JavaScript files failed to load. Now it is fixed. I apologize for the inconvenience.

I will update this place soon. In the meantime, enjoy the cold spring!


Thursday, December 13, 2012

Starting with the Sugar API


Introduction

The Sugar API is easy to use and powerful. With the API, Sugar can talk to any external systems: BI systems, ERP, ETL, ESB, custom applications, etc.
I wrote this post in order to help people who need a quick ramp up in writing custom code.

This blog will show how to retrieve records based on:
1- a record id
2- a filter on a module
3- a sql query on a module
4- a report


Quick takeaway

The Sugar API let you make awesome queries that you cannot do with other CRM systems. 


Before we start

I recommended to check my 2 others posts related to this topic:
1- SugarCRM: SOAP and REST calls using PHP
2- SugarCRM+Salesforce: Live data synchronization between Salesforce.com and SugarCRM



LOG IN YOUR INSTANCE

Today I will show you how to retrieve records using the API. I wrote the code samples in PHP, you may run them from the command line or add them to a web application.

We are working with REST web services. First of all, you need to log in with your credentials. The Sugar API does not need your password. You will use a md5 transformation. To get your md5 password, run from the command line:
php -r "echo md5('yourPassword').\"\\n\";"

Here the code to log in your instance. We will start all the PHP codes with this bloc.

Define the verbose level (none, debug, info):
use the || boolean operator to combine several log levels. This log level is define in my code and is not related to PHP or to the Sugar framework.
define('NONE'0);
define('DBG'1);
define('IFO'2);
//$dg = IFO || DBG;
$dg IFO;
In this example my server name is sugarcrm.ubuntu1.

I am calling Sugar API version 4_1 (read "What Version of the API Should I Be Using?" to get your flavor) //
// LOGIN
//
$url 'http://sugarcrm.ubuntu1/service/v4_1/rest.php';
$curl curl_init($url);
curl_setopt($curlCURLOPT_POSTtrue);
curl_setopt($curlCURLOPT_HEADERfalse);
curl_setopt($curlCURLOPT_RETURNTRANSFERtrue);
$parameters =
  array(
    
"user_auth" =>
        array(
        
'user_name' => "admin",
        
'password' => "0192023a7bbd73220516f0ffdf18b532",
        
'version' => "0.1"
        
),
    
"application_name" => ''
  
);
$json json_encode($parameters);
$postArgs 'method=login&input_type=json&response_type=json&rest_data=' $json;
if (
$dg&DBG) echo "postArgs=$postArgs\n";
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs);
$response curl_exec($curl);
$result json_decode($response);
if(!
is_object($result)) { die("Connection error\n"); }


Check if the login succeeded
if ($dg&DBG) { print_r($result); echo "\n"; }
if (isset(
$result->number)) { // Invalid Login?
    
echo $result->name "\n";
    exit;
}


Get the session id
$sessionid $result->id;
$userid=$result->name_value_list->user_id->value;
if (
$dg&DBG) echo "sessionid=$sessionid\n";
if (
$dg&DBG) echo "userid=$userid\n";

EXAMPLE 1: retrieve a record based on its ID

Copy/paste the log in code. We need the $sessionid value to make the core call.
We are making the API call get_entry. We already know the record ID. It is an account. We want the API to send back the account name and the shipping city.
//
// Get 1 account (540c66a3-de28-790b-e3b9-4fc9c84d6880)
//
// get_entry(session, module_name, id,select_fields, link_name_to_fields_array)
$parameters = array(
    
'session' => $sessionid,
    
'module_name' => 'Accounts',
    
'id' => '540c66a3-de28-790b-e3b9-4fc9c84d6880',
    
'select_fields' => array('name','shipping_address_city')
);
$json json_encode($parameters);
$postArgs 'method=get_entry&input_type=json&response_type=json&rest_data=' $json;
if (
$dg&DBG) echo "postArgs=$postArgs\n";
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs);
$response curl_exec($curl);
if (
$dg&IFO) echo "response json=$response\n";
$result json_decode($response);
if (
$dg&IFOprint_r($result);
curl_close($curl); 
Print out the result in CSV style:  

echo "id;name;city\n";
foreach (
$result->entry_list as $i) {
    echo 
$i->id.";";
    echo 
$i->name_value_list->name->value.";";
    echo 
$i->name_value_list->shipping_address_city->value."\n";
}


Sample output:

id;name;city
540c66a3-de28-790b-e3b9-4fc9c84d6880;SugarCRM Inc.;Cupertino


What we are sending & receiving? This serialized data, based on Json.
Sugar's response in Json is:
{"entry_list":[{"id":"540c66a3-de28-790b-e3b9-4fc9c84d6880","module_name":"Accounts","name_value_list":{"name":{"name":"name","value":"SugarCRM Inc."},"shipping_address_city":{"name":"shipping_address_city","value":"Cupertino"}}}],"relationship_list":[]}

In a human readable form (using print_r):

stdClass Object
(
    [entry_list] => Array
        (
            [0] => stdClass Object
                (
                    [id] => 540c66a3-de28-790b-e3b9-4fc9c84d6880
                    [module_name] => Accounts
                    [name_value_list] => stdClass Object
                        (
                            [name] => stdClass Object
                                (
                                    [name] => name
                                    [value] => SugarCRM Inc.
                                )

                            [shipping_address_city] => stdClass Object
                                (
                                    [name] => shipping_address_city
                                    [value] => Cupertino
                                )

                        )

                )

        )

    [relationship_list] => Array
        (
        )

)


the property entry_list in an array. We are looping on it. To get the account name, the PHP code is: 
$i->name_value_list->name->value



EXAMPLE 2: retrieve records in a module, based on a filter

We want to select all the accounts where the name starts with the letter 'S'.
The SQL query is: 
select id,name from accounts where name like 's%';
We use the core call get_entry_list
Remark: as Sugar is making a soft delete of the records, we need to specify that we are filtering on the existing records with:
 'deleted' => '0'
Here is the code, straightforward:

//
// Get accounts "select id,name from accounts where name like 's%'"
//
//get_entry_list(session, module_name, query, $order_by,offset, select_fields,
//               link_name_to_fields_array, max_results, deleted)

$parameters = array(
    
'session' => $sessionid,
    
'module_name' => 'Accounts',
    
'query' => "name like 's%'",
    
'name',
    
'offset' => 0,
    
'select_fields' => array('id''name'),
    
'link_name_to_fields_array' => array(),
    
'max_results' => '100',
    
'deleted' => '0'
);
$json json_encode($parameters);
$postArgs 'method=get_entry_list&input_type=json&response_type=json&rest_data=' $json;
echo 
"postArgs=$postArgs\n";
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs);
$response curl_exec($curl);
$result json_decode($response);
if (
$dg<=IFOprint_r($result);
curl_close($curl); 

echo 
"id;name\n";
foreach (
$result->entry_list as $i) {
    echo 
$i->id.";";
    echo 
$i->name_value_list->name->value."\n";
}



EXAMPLE 3: retrieve records using a SQL query on a module

We are synchronizing with an external system and need to know which accounts had been modified in the past 3 days. The SQL query is:
select id,name from accounts where date_modified > DATE_ADD(NOW(), INTERVAL -3 DAY);
We do not need to handle date format or date construction, the database is making the job. We are using the core call get_entry_list.

Here is the code:

//
// Get accounts "select id,name from accounts where date_modified > DATE_ADD(NOW(), INTERVAL -3 DAY)
//
//get_entry_list(session, module_name, query, $order_by,offset, select_fields,
//               link_name_to_fields_array, max_results, deleted)

$parameters = array(
    
'session' => $sessionid,
    
'module_name' => 'Accounts',
    
'query' => "date_modified > DATE_ADD(NOW(), INTERVAL -3 DAY)",
    
'name',
    
'offset' => 0,
    
'select_fields' => array('id''name''date_modified'),
    
'link_name_to_fields_array' => array(),
    
'max_results' => '100',
    
'deleted' => '0'
);
$json json_encode($parameters);
$postArgs 'method=get_entry_list&input_type=json&response_type=json&rest_data=' $json;
if (
$dg&DBG) echo "postArgs=$postArgs\n";
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs);
$response curl_exec($curl);
$result json_decode($response);
if (
$dg&IFOprint_r($result);
curl_close($curl); 

echo 
"id;name\n";
foreach (
$result->entry_list as $i) {
    echo 
$i->id.";";
    echo 
$i->name_value_list->name->value.";";
    echo 
$i->name_value_list->date_modified->value."\n";
}




EXAMPLE 4: retrieve records using a report

You need a commercial edition of Sugar.
We want to run a report using the API. The report is a standard report called "All Open Opportunities". We are using the core call get_report_entries and provide the report ID and the fields.


//get_report_entries(session,ids,select_fields)
// report Id=3be50900-e1bb-2da8-be27-4fc769f9f8c1, Name="All Open Opportunities"

$parameters = array(
    
'session' => $sessionid,
    
'id' => array('3be50900-e1bb-2da8-be27-4fc769f9f8c1'),
    
'select_fields' => array('id''name''date_modified'),
);
$json json_encode($parameters);
$postArgs 'method=get_report_entries&input_type=json&response_type=json&rest_data=' $json;
if (
$dg&DBG) echo "postArgs=$postArgs\n";
curl_setopt($curlCURLOPT_POSTFIELDS$postArgs);
$response curl_exec($curl);
$result json_decode($response);
if (
$dg&IFOprint_r($result);
curl_close($curl); 

foreach (
$result->field_list[0] as $i)
    echo 
$i->label.";";
echo 
"\n";

foreach (
$result->entry_list[0] as $record) {
    if (!isset(
$record->name_value_list)) continue;
    foreach (
$record->name_value_list as $i) echo $i->value.";";
    echo 
"\n";
}



The output in CSV style:

Opportunity Name;Type;Sales Stage;Expected Close Date;Amount;User Name;
Calm Sailing Inc - 1000 units;New Business;Qualification;2012-06-01;$75,000.00;will;
Anytime Air Support Inc - 1000 units;Existing Business;Id. Decision Makers;2012-06-11;$25,000.00;max;
Income Free Investing LP - 1000 units;Existing Business;Id. Decision Makers;2012-06-25;$25,000.00;chris;
Airline Maintenance Co - 1000 units;New Business;Perception Analysis;2012-06-29;$75,000.00;sarah;
etc.



Have fun with the Sugar API !