Function
When enabling this module, you enable usage of web services provided by Werpos server. You can then make REST call of different web services provided by Werpos.
Werpos REST web services server
Once module webservices REST is activated, Werpos become also a server of REST web services. So you can send your own REST request to relative URL /api/index.php/xxx where xxx is name of the API to call.
A list of APIs on your installation can be found via the explorer.
Apache setup
There is nothing to do. If your Werpos is working with Apache, the REST API should also work. APIs will be served by the same virtual web server than your application.
Nginx setup
Like Apache, if your Werpos is working inside a NGinx virtual host, you should have nothing to do to have your API working. APIs will be served by the same virtual web server than your application.
If you experience troubles, you can try to edit your NGinx config file to match the following example. This is a simple localhost setup with a working REST API handling (tested with 9.0.1 on parabola gnu/linux).
worker_processes 1;
error_log /var/log/nginx/error.log;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name Werpos.localhost; # adjust to your domain
root /usr/share/webapps/Werpos/htdocs; # adjust to your path
index index.php;
# from https://github.com/Werpos/Werpos/issues/6163#issuecomment-391265538
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
# Mitigate https://httpoxy.org/ vulnerabilities
fastcgi_param HTTP_PROXY "";
root /usr/share/webapps/Werpos/htdocs;
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Werpos Rest API path support
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;
}
}
}
List of Provided Services
Only few services are available. Starting to Werpos 5.0 version, you can see full list of Werpos Web services provided, by calling the explorer here at address:
http://yourWerposurl/api/index.php/explorer
You must first make the first call to the login api to get the API key. Then enter api key to get list of all other available API services.
For example, you can try the explorer on the demo instance at:
https://demo.Werpos.org/api/index.php/explorer
You must first make the first call to the login api to get the API key. Then enter api key to get list of all other available API services.
You can then test directly from this explorer any API. This is the recommended solution to test any Werpos API since any API and parameters is documented here. As a result of any test, you will get the answer but also example on how to call the API from command line using curl.
Installation
To install, open the modules page and activate de API REST module. On the configuration page of the module, there’s a link to an explorer. Click on it to open the API explorer.
On the top right corner, paste the <token> of the user you want to use to call the API and click the “explore” button. Note: The token of each user can be defined on the user record page.
After clicking on “Explore”, you should see all the actions available with this token. If you don’t have a lot of actions, it’s probably because the according modules are not activated. If you want to see the invoices, you have to activate the invoice module in the configuration of Werpos. Same for products, third parties and so on.
On this exploration page of the API, you can do quite a lot of tests. Reading datas from Werpos and writing, modifying and deleting as well. Warning: Data are really modified in your database.
Use
To use the REST API, you have to call an url such as this one : http://<my_server>/api/index.php/<action>
with one of the 4 following methods : GET, POST, PUT, DELETE, replacing <action> by the action you want to use. Ex : http://<my_server>/api/index.php/invoices
There’s different ways to do so. Here’s a piece of code but you can also use libraries such as phphttpclient.com
// Example of function to call a REST API
function callAPI($method, $apikey, $url, $data = false)
{
$curl = curl_init();
$httpheader = [‘DOLAPIKEY: ‘.$apikey];
switch ($method)
{
case “POST”:
curl_setopt($curl, CURLOPT_POST, 1);
$httpheader[] = “Content-Type:application/json”;
if ($data)
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
break;
case “PUT”:
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, ‘PUT’);
$httpheader[] = “Content-Type:application/json”;
if ($data)
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
break;
default:
if ($data)
$url = sprintf(“%s?%s”, $url, http_build_query($data));
}
// Optional Authentication:
// curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
// curl_setopt($curl, CURLOPT_USERPWD, “username:password”);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HTTPHEADER, $httpheader);
$result = curl_exec($curl);
curl_close($curl);
return $result;
}
This is just a working example. There’s no error control and security was not in mind but you can use this code, modify it to suit your needs
The fonction has 4 parameters :
- $method : string, “GET”, “POST”, “PUT”, “DELETE”
- $apikey : string, “your <token> generated earlier”
- $url : string, url to call. Ex : “http://<my_server>/api/index.php/invoices”
- $data : string, datas in json format. This parameter is not mandatory.
Examples with PHP
This is more examples, for different uses cases:
In every examples, we have:
- $apiKey = “<my token>”;
- $apiUrl = “http://<my_server>/api/index.php/”;
// Retrieve products list
$listProduits = [];
$produitParam = [“limit” => 10000, “sortfield” => “rowid”];
$listProduitsResult = CallAPI(“GET”, $apiKey, $apiUrl.”products”, $produitParam);
$listProduitsResult = json_decode($listProduitsResult, true);
if (isset($listProduitsResult[“error”]) && $listProduitsResult[“error”][“code”] >= “300”) {
} else {
foreach ($listProduitsResult as $produit) {
$listProduits[intval($produit[“id”])] = html_entity_decode($produit[“ref”], ENT_QUOTES);
}
}
Comments :
- I retrieve the 10’000 first products sorted on their ID in the base
- html_entity_decode is necessary as single quotes are encoded
- It’s easy to use the same method (replacing products with dictionnarycountries) to retrieve the list of countries and their ID
// Create a product
$ref = “my_product_ref_X203ZZ”;
$newProduct = [
“ref” => $ref,
“label” => $ref
];
$newProductResult = CallAPI(“POST”, $apiKey, $apiUrl.”products”, json_encode($newProduct));
$newProductResult = json_decode($newProductResult, true);
Comments :
- before creating a product, it could be wise to check if it exists. Using my first example, you’ll have :
// my ref
$ref = “my_product_ref_X203ZZ”;
// does it exist in my array
$produitKey = array_search($ref, $listProduits);
if ($produitKey) {
// yes
$fk_product = $produitKey;
} else {
// no
// Create the product
$newProduct = [
“ref” => $ref,
“label” => $ref
];
$newProductResult = CallAPI(“POST”, $apiKey, $apiUrl.”products”, json_encode($newProduct));
$newProductResult = json_decode($newProductResult, true);
if (isset($newProductResult[“error”]) && $newProductResult[“error”][“code”] >= “300”) {
// there’s been an error
echo “<pre>ERROR”, var_dump($newProductResult), “</pre>”;
exit;
} else {
// everything good
$fk_product = $newProductResult;
$listProduits[$fk_product] = $ref;
}
}
Comments :
- I check if the ref of my product exist in the array created in the first example.
- If it exists, I use it’s key in the array as ID
- If it doesn’t exist, I create the product, then I add it to my array for next time then I use the ID created
- I choose this method to do less API calls when I have to import 500 orders. I just need to retrieve the products list once at the beginning instead of searching Werpos each time.
Warning: The example below is not working properly. Creating an order and lines in one go is not supported. Instead you have to create an order first and with the returned order id add the lines using the /orders/{id}/lines endpoint.
// create an order with 2 products
// The array where there will be all the products lines of my order.
$newCommandeLine = [];
// product 1
$ref1 = “my_product_ref_X203ZZ”;
$prix1 = 10;
$qtt1 = 100;
$tva1 = 20;
$fk_product1
// product 2
$ref2 = “my_product_ref_B707FD”;
$prix2 = 13;
$qtt2 = 37;
$tva2 = 20;
$newCommandeLine[] = [
“desc” => $ref1,
“subprice” => $prix1,
“qty” => $qtt1,
“tva_tx” => floatval($tva1),
“fk_product”=> $fk_product1
];
$newCommandeLine[] = [
“desc” => $ref2,
“subprice” => $prix2,
“qty” => $qtt2,
“tva_tx” => floatval($tva2),
“fk_product”=> $fk_product2
];
if (count($newCommandeLine) > 0) {
$newCommande = [
“socid” => $clientDoliId,
“type” => “0”,
“lines” => $newCommandeLine,
“note_private” => “order created automatically with API”,
];
$newCommandeResult = CallAPI(“POST”, $apiKey, $apiUrl.”orders”, json_encode($newCommande));
$newCommandeResult = json_decode($newCommandeResult, true);
}
Comments :
- $clientDoliId is the ID of the customer in the Werpos database. Either you know it or you can search for it before
- type => 0, This is a customer order (while 1 = supplier order)
// Validate an order
$newCommandeValider = [
“idwarehouse” => “0”,
“notrigger” => “0”
];
$newCommandeValiderResult = CallAPI(“POST”, $apiKey, $apiUrl.”orders/”.$newCommandeResult.”/validate”, json_encode($newCommandeValider));
$newCommandeValiderResult = json_decode($newCommandeValiderResult, true);
Comments :
- in this example, on the penultimate line, we can see : $apiUrl.”orders/”.$newCommandeResult.”/validate”.
$newCommandeResult is the ID of the order created in the previous example
// search in the database if a customer exist
$customer_name = “Acme Inc”;
$clientSearch = json_decode(CallAPI(“GET”, $apiKey, $apiUrl.”thirdparties”, array(
“sortfield” => “t.rowid”,
“sortorder” => “ASC”,
“limit” => “1”,
“mode” => “1”,
“sqlfilters” => “(t.nom:=:'”.$customer_name.”‘)”
)
), true);
Comments :
- limit => 1 only 1 customer
- mode => 1 we are looking for a customer, not a supplier (they are thirdparties too but with a different status)
- sqlfilters a bit special but there are more examples on the explorer page
// customer doesn’t exist. Let’s create it and get it’s ID
$newClient = [
“name” => “customer company name”,
“email” => “customer company email”,
“client” => “1”,
“code_client” => “-1”
];
$newClientResult = CallAPI(“POST”, $apiKey, $apiUrl.”thirdparties”, json_encode($newClient));
$newClientResult = json_decode($newClientResult, true);
$clientDoliId = $newClientResult;
Comments :
- client => 1 He is a customer (not a supplier)
- code_client => -1 so the customer code will be generated automatically.
- we get the customer ID in $clientDoliId
Develop your own service/ API
Adding a new REST service is as easy than adding a file called api_mymoduleobject.class.php into the directory htdocs/mymodule/class. You take the example into htdocs/commande/class/api_orders.class.php
Note that if you use the modulebuilder tool, the file may be generated for you.
The framework detects automatically the API and it should be visible into the explorer.
Method and parameters are detected according to introspection done into PHP class of the object (htdocs/mymodule/class/object.class.php) using the annotations found into the class. For a documentation about annotation: https://github.com/Luracast/Restler/blob/master/ANNOTATIONS.md
These are just the basics.
Some more informations in the Werpos code. You can look here : htdocs/<directory>/class/api_xxx_class.php
Ex : htdocs/societe/class/api_thirdparties.class.php for thirdparties.
Ex : Invoices are in htdocs/compta/facture/class