KwaMoja comes with a simple to use and flexible API that allows client programs to access KwaMoja in a safe and secure manner. If you wish to write an application that accesses KwaMoja, either to post transactions, or to extract information, then you should use the API, rather than try to access the KwaMoja database directly, as the API makes sure that the integrity of the data is maintained. The API is an "Application Program Interface", that is intended to expose functionality to external programs. There are currently a number of low level functions it exposes to enable external applications to retrieve data and to update or insert data. However the API was structured in a manner that allows other protocols to be used very easily. All that would need to be done to use the SOAP protocol for instance would be to create an api_soap.php file with the same functions as the api_xmlrpc.php file.
The API uses the XML-RPC protocol to communicate between the client and the server. This was chosen because it is lightweight, simple, and easy to use. The API uses the XML-RPC for PHP - also called the phpxmlrpc class from Useful Inc originally developed by Edd Dumbill. This is an external library that is bundled with KwaMoja code ready to run. Whilst the standard PHP XML-RPC extension could have been used, the extension is often not installed by default, so would add another dependency to KwaMoja and complexity for the setup and installation of KwaMoja.
XML-RPC is a protocl to use XML to make RPC - remote procedure calls.
Simply put the XML-RPC call is XML that contains the method of the remote procedure call together with any parameters and their data types and is sent over http as a POST to the XML-RPC server - the server returns an XML payload containing the results of the call. The parameters sent to the methods can contain arrays and associative arrays of data.
The clever thing about XML-RPC is that it is the simplest protocol around for doing web-services. The newer and MUCH more complex SOAP - Simple Object Access Protocol - is quite involved and complicated. is founded on the KISS principle.
In fact the XML-RPC "Server" in is just the script http://www.yourdomain.com//api/api_xml-rpc.php
There is no daemon background process running continuously to field calls to the "server" it is just a script that is http posted to by the XML-RPC call sending the XML encoded method to be run together with the necessary parameters to the API - the server script runs the API php functions exposed by the xml-rpc methods and returns the XML-RPC response as an XML payload. The phpxmlrpc class does the packaging converting the PHP variables and arrays to the XML required for the XML-RPC call and also has the functions to convert the XML response into something useable in PHP without having to write the XML parsing routines.
There is one hardcoded parameter that needs to be set in the api before you start to use it. The database name - the company database - to use with the api is defined in the file api/api_php.php - the variable
$api_DatabaseName="demo";
should be set before attempting to use the api.
It is worthwhile reading a how-to on XML-RPC with PHP which explains in more detail what is going on as a primer for the concepts.
The beauty of XML-RPC is that the client calling the XML-RPC server and performing native functions can be called from any language (with XML-RPC bindings). I have used Vala, Genie and Python. Python particularly has been very straight forward as it has an xmlrpclib bundled with it. Of course a PHP client is also possible and is demonstrated below.
The API help is actually produced by an xml-rpc call to the API using the system.listMethods method (this is a phpxmlrpc method - not a API method). Aother system xml-rpc method of phpxmlrpc class is used to return the details of each method"s parameters required. So the help file not only documents each of the API methods it is itself and illustration of how the API can be used!!
In the narrative below, the word "Server" is used to refer to the host KwaMoja installation - in fact the "server" this is the script KwaMoja/xml-rpc/api_xmlrpc.php
The API is configured by default to use the company database KwaMojademo, and this is hard coded in the script api/api_php.php on line 6:
$api_DatabaseName="KwaMojademo";
This database should be changed manually before the API can be used to the company database that you wish the API to access. Note that the API can only work on one company in a KwaMoja installation. This is a limitation of the design.
Below is a simple example of how to use the API.
It is a simple client (a consumer of the API) application where the stock quantity for the item DVD-TOPGUN is retrieved from the KwaMoja installation using the API.
echo "Test API"; //the xmlrpc class can output some funny warnings so make sure notices are turned off error_reporting (E_ALL & ~E_NOTICE); /* you need to include the phpxmlrpc class - see link above - copy the whole directory structure of the class over to your client application from the /xmlrpc directory */ include ("xmlrpc/lib/xmlrpc.inc"); //if your install is on a server at http://www.yourdomain.com/ $ServerURL = "http://www.yourdomain.com//api/api_xml-rpc.php"; $DebugLevel = 0; //Set to 0,1, or 2 with 2 being the highest level of debug info $Parameters = array(); /* The trap for me was that each parameter needs to be run through xmlrpcval() - to create the necessary xml required for the rpc call if one of the parameters required is an array then it needs to be processing into xml for the rpc call through php_xmlrpc_encode()*/ $Parameters["StockID"] = new xmlrpcval("DVD-TOPGUN"); //the stockid of the item we wish to know the balance for //assuming the demo username and password will work ! $Parameters["Username"] = new xmlrpcval("admin"); $Parameters["Password"] = new xmlrpcval(""); $Msg = new xmlrpcmsg(".xmlrpc_GetStockBalance", $Parameters); $Client = new xmlrpc_client($ServerURL); $Client->setDebug($DebugLevel); $Response = $Client->send($Msg); $Answer = php_xmlrpc_decode($Response->value()); if ($Answer[0]!=0){ //then the API returned some errors need to figure out what went wrong //need to figure out how to return all the error descriptions associated with the codes } else { //all went well the returned data is in $answer[1] //answer will be an array of the locations and quantity on hand for DVD_TOPGUN so we need to run through the array to print out for ($i=0; $i < sizeof($Answer[1]);$i++) { echo "" . $Answer[1][$i]["loccode"] . " has " . $Answer[1][$i]["quantity"] . " on hand"; } }
To create invoices in you need to use the following methods:
InsertOrderHeader InsertOrderLine - potentially multiple times for all the lines on the order then InvoiceSalesOrder - to invoice sales orders directly assuming the entire order is delivered - it cannot deal with controlled stock items though. However, it does process invoices in much the same way as standard with updates to the stock quantities dispatched, GL entries and records required to record taxes and sales analysis records.
To create a credit note just a sinlge API call is required:
CreateCreditNote - to create a credit note from some base header data and an array of line items (as an associative array. In the same way as the InvoiceSalesOrder function this does all the same processing as a standard credit note from the interface in .
There are some example scripts on the wiki showing how a number of the API XML-RPC functions are called - these scripts should be put on a web-server outside a installation - all you need to do is edit the config.inc file to give the system your username and password and the URL of your installation you wish to connect to. As always playing with the examples helps to figure out how it all works.
For this example we will build a small PHP application that will first interrogate KwaMoja for a full list of available stock locations, build them into an HTML drop down list and then allow a user to input a stock item code and return the quantity of stock of that item at the selected location. We will use PHP for simplicity but any language that has an xmlrpc library (just about every language) can be used to write a client.
Firstly we need the xmlrpc library, so we copy the xmlrpc sub-directory from KwaMoja to this new project.
The basic code will look like this, and be saved into a file called index.php:
<html> <head> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> </head> <body> <form action="index.html" method="post"> Stock Code:<input type="text" name="StockID" /><br /> Location:<select name="location"> <?php // Here will go the available stock locations from KwaMoja?> </select><br /> <input type="submit" name="submit" value="Submit" /> </form> </body> </html>
As its name suggests, the xmlrpc function calls are made by sending an XML file with the function name and the parameters to the server, and receive an XML file back from the server.
To assist with this, the phpxmlrpc library that KwaMoja uses (and we will use as well for our client) contains methods to encode our function call as XML, and to decode the XML that we receive back.
First off we need to include the xmlrpc library in our file, so immediately above the HTML, we need the following:
<?php include "xmlrpc/lib/xmlrpc.inc"; $xmlrpc_internalencoding="UTF-8"; include "xmlrpc/lib/xmlrpcs.inc"; ?>
To populate the drop down box with the stock locations in it the API function called KwaMoja.xmlrpc_GetLocationList() is used. This function takes two parameters, a valid userid for the KwaMoja instance, and the password for that user. Using the demo credentials is admin/KwaMoja. Also, the file api_php.php must have the $api_databasename set to whatever the name of the database is on your target KwaMoja installation.
The function to get the inventory locations will look like this, and be at the bottom of the file, within a PHP code section (ie <?php ?>).
function GetLocations() { //Encode the user/password combination $UserID = php_xmlrpc_encode("admin"); $Password = php_xmlrpc_encode("KwaMoja"); //Create a client object to use for xmlrpc call $Client = new xmlrpc_client("http://localhost/KwaMoja/api/api_xml-rpc.php"); //Create a message object, containing the parameters and the function name $Message = new xmlrpcmsg("KwaMoja.xmlrpc_GetLocationList", array($UserID, $Password));
The most common error just return no authorisation with no information about why this has happened. Most KwaMoja API/XML-RPC calls returns an array containing two elements, the first - $Response[0] containing an integer code, and the second the result of the call, if one is expected. If the integer code is zero, this indicates success. Any other code indicates an error. These are the errors and the codes that are returned to represent them:
NoAuthorisation - 1 IncorrectDebtorNumberLength - 1000 DebtorNoAlreadyExists - 1001 IncorrectDebtorNameLength - 1002 InvalidAddressLine - 1003 CurrencyCodeNotSetup - 1004 SalesTypeNotSetup - 1005 InvalidClientSinceDate - 1006 HoldReasonNotSetup - 1007 PaymentTermsNotSetup - 1008 InvalidDiscount - 1009 InvalidPaymentDiscount - 1010 InvalidLastPaid - 1011 InvalidLastPaidDate - 1012 InvalidCreditLimit - 1013 InvalidInvAddrBranch - 1014 InvalidDiscountCode - 1015 InvalidEDIInvoices - 1016 InvalidEDIOrders - 1017 InvalidEDIReference - 1018 InvalidEDITransport - 1019 InvalidEDIAddress - 1020 InvalidEDIServerUser - 1021 InvalidEDIServerPassword - 1022 InvalidTaxRef - 1023 InvalidCustomerPOLine - 1024 DatabaseUpdateFailed - 1025 NoDebtorNumber - 1026 DebtorDoesntExist - 1027 IncorrectBranchNumberLength - 1028 BranchNoAlreadyExists - 1029 IncorrectBranchNameLength - 1030 InvalidEstDeliveryDays - 1031 AreaCodeNotSetup - 1032 SalesmanCodeNotSetup - 1033 InvalidFwdDate - 1034 InvalidPhoneNumber - 1035 InvalidFaxNumber - 1036 InvalidContactName - 1037 InvalidEmailAddress - 1038 LocationCodeNotSetup - 1039 TaxGroupIdNotSetup - 1040 ShipperNotSetup - 1041 InvalidDeliverBlind - 1042 InvalidDisableTrans - 1043 InvalidSpecialInstructions - 1044 InvalidCustBranchCode - 1045 BranchNoDoesntExist - 1046 StockCodeDoesntExist - 1047 StockCategoryDoesntExist - 1048 IncorrectStockDescriptionLength - 1049 IncorrectUnitsLength - 1050 IncorrectMBFlag - 1051 InvalidCurCostDate - 1052 InvalidActualCost - 1053 InvalidLowestLevel - 1054 InvalidDiscontinued - 1055 InvalidEOQ - 1056 InvalidVolume - 1057 InvalidKgs - 1058 IncorrectBarCodeLength - 1059 IncorrectDiscountCategory - 1060 TaxCategoriesDoesntExist - 1061 InvalidSerialised - 1062 IncorrectAppendFile - 1063 InvalidPerishable - 1064 InvalidDecmalPlaces - 1065 IncorrectLongStockDescriptionLength - 1066 StockCodeAlreadyExists - 1067 TransactionNumberAlreadyExists - 1068 InvalidTranDate - 1069 InvalidSettled - 1070 IncorrectReference - 1071 IncorrectTpe - 1072 InvalidOrderNumbers - 1073 InvalidExchangeRate - 1074 InvalidOVAmount - 1075 InvalidOVGst - 1076 InvalidOVFreight - 1077 InvalidDiffOnExchange - 1078 InvalidAllocation - 1079 IncorrectInvoiceText - 1080 InvalidShipVia - 1081 InvalidEdiSent - 1082 InvalidConsignment - 1083 InvalidLastCost - 1084 InvalidMaterialCost - 1085 InvalidLabourCost - 1086 InvalidOverheadCost - 1087 InvalidCustomerRef - 1088 InvalidBuyerName - 1089 InvalidComments - 1090 InvalidOrderDate - 1091 InvalidDeliverTo - 1092 InvalidFreightCost - 1094 InvalidDeliveryDate - 1095 InvalidQuotationFlag - 1096 OrderHeaderNotSetup - 1097 InvalidUnitPrice - 1098 InvalidQuantity - 1099 InvalidDiscountPercent - 1100 InvalidNarrative - 1101 InvalidItemDueDate - 1102 InvalidPOLine - 1103 GLAccountCodeAlreadyExists - 1104 IncorrectAccountNameLength - 1105 AccountGroupDoesntExist - 1106 GLAccountSectionAlreadyExists - 1107 IncorrectSectionNameLength - 1108 GLAccountGroupAlreadyExists - 1109 GLAccountSectionDoesntExist - 1110 InvalidPandL - 1111 InvalidSequenceInTB - 1112 GLAccountGroupDoesntExist - 1113 InvalidLatitude - 1114 InvalidLongitude - 1115 CustomerTypeNotSetup - 1116 NoPricesSetup - 1117 InvalidInvoicedQuantity - 1118 InvalidActualDispatchDate - 1119 InvalidCompletedFlag - 1120 InvalidCategoryID - 1121 InvalidCategoryDescription - 1122 InvalidStockType - 1123 GLAccountCodeDoesntExists - 1124 StockCategoryAlreadyExists - 1125 SupplierNoAlreadyExists - 1126 IncorrectSupplierNameLength - 1127 InvalidSupplierSinceDate - 1128 InvalidBankAccount - 1129 InvalidBankReference - 1130 InvalidBankPartics - 1131 InvalidRemittanceFlag - 1132 FactorCompanyNotSetup - 1133 SupplierNoDoesntExists - 1134 InvalidSuppliersUOM - 1135 InvalidConversionFactor - 1136 InvalidSupplierDescription - 1137 InvalidLeadTime - 1138 InvalidPreferredFlag - 1139 StockSupplierLineDoesntExist - 1140 InvalidRequiredByDate - 1141 InvalidStartDate - 1142 InvalidCostIssued - 1143 InvalidQuantityRequired - 1144 InvalidQuantityReceived - 1145 InvalidStandardCost - 1146 IncorrectSerialNumber - 1147 WorkOrderDoesntExist - 1148 InvalidIssuedQuantity - 1149 InvalidTransactionDate - 1150 InvalidReceivedQuantity - 1151 ItemNotControlled - 1152 ItemSerialised - 1153 BatchNumberDoesntExist - 1154 BatchIsEmpty - 1155 NoSuchArea - 1156 NoSuchSalesMan - 1157 NoCompanyRecord - 1158 NoReadOrder - 1159 NoReadOrderLines - 1160 NoTaxProvince - 1161 TaxRatesFailed - 1162 NoReadCustomerBranch - 1163 NoReadItem - 1164 MustBeReceiptOrCreditNote - 1165 NoTransactionToAllocate - 1166
As you can see error code 1 indicates "NoAuthorisation" which will be the error returned if the user name or password is incorrect and also if the name of the database to be used in the api call is not specified correctly in the file KwaMoja/api/api_php.php in the variable:
$api_DatabaseName="KwaMojademo";
To catch the errors we create a session variable to hold any error messages that happen, so that we can show the to the user. So the initialisation code at the top of index.php becomes:
<?php include "xmlrpc/lib/xmlrpc.inc"; $xmlrpc_internalencoding="UTF-8"; include "xmlrpc/lib/xmlrpcs.inc"; $_SESSION["Errors"] = array(); ?>
and then at the bottom of the output we have a loop to output these errors:
foreach ($_SESSION["Errors"] as $Error) { echo $Error; }
if ($ReturnValue[0] == 0) { return $ReturnValue[1]; } elseif ($ReturnValue[0] == 1) { $_SESSION["Errors"][] = "Incorrect login/password credentials used"; }
} elseif ($ReturnValue[0] == 1) { $_SESSION["Errors"][] = "Incorrect login/password credentials used"; } elseif ($ReturnValue[0] == 1047) { $_SESSION["Errors"][] = "The stock code you entered does not exist"; }
function LocationName($LocationCode) { //Encode the data items $UserID = php_xmlrpc_encode("admin"); $Password = php_xmlrpc_encode("KwaMoja"); $Code = php_xmlrpc_encode($LocationCode); //Create a client object to use for xmlrpc call and set its debug level to zero $Client = new xmlrpc_client("http://localhost/KwaMoja/api/api_xml-rpc.php"); $Client->setDebug(0); //Create a message object, containing the parameters and the function name $Message = new xmlrpcmsg("KwaMoja.xmlrpc_GetLocationDetails", array($Code, $UserID, $Password)); //Use the client object to send the message object to the server, returning the response $Response = $Client->send($Message); //Decode the response and return the array $ReturnValue = php_xmlrpc_decode($Response->value()); if ($ReturnValue[0] == 0) { return $ReturnValue[1]["locationname"]; } }
The first section encodes the parameters as XML. The first two parameters are always the userid/password combination, and for this function call we need a third parameter, which is the code of the location that we require the name of. The second section is identical to the previous function and creates an instance of the XML-RPC client class. The third section then creates an instance of the message class, with the first parameter being the full name of the API function being called, in this case KwaMoja.xmlrpc_GetLocationDetails, and then the second parameter is an array of the encoded parameters, (location code, userid, password). This message is then sent to the server, and the response decoded into an array called $ReturnValue.
As last time the first element of the array signifies whether the function was successful (a zero), or any other integer for an error code. The second element is an associative array of details for that location. The key of each element is the field name for that value. In our case we just want the location name, so we return the element ["locationname"]. If it was the telephone number we were interested in we would just return the ["tel"] element.
Changing the line in the HTML where we fill the drop down box to:
echo <option value=" . $LocationCode . "> . LocationName($LocationCode) . "</option>";
The full name of the location appears in the drop down the list, but the value returned by the form is still just the code.
All that is left to complete our client, is to type a stock code in the text box, submit the form and return the amount of stock for that code at the chosen location. First we need to insert some PHP code in the HTML to handle the form being sent:
if (isset($_POST["submit"])) { echo "The quantity of " . $_POST["StockID"] . " at " . $_POST["location"] . " is : " . GetStockQuantity($_POST["StockID"], $_POST["location"]); }
As you can see this calls another PHP function - GetStockQuantity() - that retrieves the stock quantity for the required item at the required location. Looking at the API function reference in the manual the API function we require is KwaMoja.xmlrpc_GetStockBalance. However this time there is a small addition we require as this function returns an array containing the stock balances at all the locations for the given stock item.
The full code for the PHP function is:
function GetStockQuantity($StockID, $LocationCode) { //Encode the data items $UserID = php_xmlrpc_encode("admin"); $Password = php_xmlrpc_encode("KwaMoja"); $StockCode = php_xmlrpc_encode($StockID); //Create a client object to use for xmlrpc call and set its debug level to zero $Client = new xmlrpc_client("http://localhost/KwaMoja/api/api_xml-rpc.php"); $Client->setDebug(0); //Create a message object, containing the parameters and the function name $Message = new xmlrpcmsg("KwaMoja.xmlrpc_GetStockBalance", array($StockCode, $UserID, $Password)); //Use the client object to send the message object to the server, returning the response $Response = $Client->send($Message); //Decode the response and return the array $ReturnValue = php_xmlrpc_decode($Response->value()); if ($ReturnValue[0] == 0) { $Items = $ReturnValue[1]; for ($i=0; $i < sizeOf($Items); $i++) { if ($Items[$i]["loccode"]==$LocationCode) { return $Items[$i]["quantity"]; } } } }
I wont go through this in details as it is mostly the same as the previous functions. The key section is the last:
$ReturnValue = php_xmlrpc_decode($Response->value()); if ($ReturnValue[0] == 0) { $Items = $ReturnValue[1]; for ($i=0; $i < sizeOf($Items); $i++) { if ($Items[$i]["loccode"]==$LocationCode) { return $Items[$i]["quantity"]; } } }