Para este ejemplo vamos a construir una pequeña aplicación PHP que primero interrogará a webERP para obtener una lista completa de las ubicaciones de existencias disponibles, las construirá en una lista desplegable HTML y luego permitirá que un usuario ingrese un código de artículo de existencias y devuelva la cantidad de existencias de ese artículo en la ubicación seleccionada. Usaremos PHP para simplificar, pero cualquier lenguaje que tenga una biblioteca xmlrpc (casi todos los idiomas) se puede usar para escribir un cliente.
En primer lugar necesitamos la biblioteca xmlrpc, por lo que copiamos el subdirectorio xmlrpc de webERP a este nuevo proyecto.
El código básico se verá así, y se guardará en un archivo llamado 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 webERP?>
</select><br />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>
Como su nombre indica, las llamadas a la función xmlrpc se realizan enviando un archivo XML con el nombre de la función y los parámetros al servidor, y reciben un archivo XML del servidor.
Para ayudar con esto, la biblioteca phpxmlrpc que webERP usa (y la usaremos también para nuestro cliente) contiene métodos para codificar nuestra llamada de función como XML y para descodificar el XML que recibimos de regreso.
En primer lugar, debemos incluir la biblioteca xmlrpc en nuestro archivo, por lo que inmediatamente antes del HTML, necesitamos lo siguiente:
<?php
include "xmlrpc/lib/xmlrpc.inc";
$xmlrpc_internalencoding="UTF-8";
include "xmlrpc/lib/xmlrpcs.inc";
?>
Para rellenar el cuadro desplegable con las ubicaciones de existencias se utiliza la función API denominada webERP.xmlrpc_GetLocationList (). Esta función toma dos parámetros, un ID de usuario válido para la instancia webERP y la contraseña para este usuario. El uso de las credenciales de demostración es admin/weberp. Además, el archivo ipa_php.php debe tener el $api_databasename configurado como el nombre de la base de datos en la instalación webERP de destino.
La función para obtener las ubicaciones de inventario se verá así, y estará en la parte inferior del archivo, dentro de una sección de código PHP (es decir, <?Php?>).
function GetLocations() {
//Encode the user/password combination
$UserID = php_xmlrpc_encode("admin");
$Password = php_xmlrpc_encode("webERP");
//Create a client object to use for xmlrpc call
$Client = new xmlrpc_client("http://localhost/webERP/api/api_xml-rpc.php");
//Create a message object, containing the parameters and the function name
$Message = new xmlrpcmsg("webERP.xmlrpc_GetLocationList", array($UserID, $Password));
El error más común simplemente no devuelve ninguna autorización sin información acerca de por qué esto ha sucedido. La mayoría de las llamadas webERP API/XML-RPC devuelve una matriz que contiene dos elementos, el primero - $Response[0] que contiene un código entero, y el segundo el resultado de la llamada, si se espera uno. Si el código entero es cero, esto indica éxito. Cualquier otro código indica un error. Estos son los errores y los códigos que se devuelven para representarlos:
NoAuthorization |
No autorizado |
1 |
IncorrectDebtorNumberLength |
Longitud incorrecta del número de deudor |
1000 |
DebtorNoAlreadyExists |
El deudor ya no existe |
1001 |
IncorrectDebtorNameLength |
Longitud incorrecta del nombre del deudor |
1002 |
InvalidAddressLine |
Línea de dirección inválida |
1003 |
CurrencyCodeNotSetup |
Código de moneda no configurado |
1004 |
SalesTypeNotSetup |
Tipo de venta no configurado |
1005 |
InvalidClientSinceDate |
Cliente inválido desde fecha |
1006 |
HoldReasonNotSetup |
Mantener la razón no configurada |
1007 |
PaymentTermsNotSetup |
Condiciones de pago no configuradas |
1008 |
InvalidDiscount |
Descuento no válido |
1009 |
InvalidPaymentDiscount |
Descuento de pago no válido |
1010 |
InvalidLastPaid |
Último pago no válido |
1011 |
InvalidLastPaidDate |
Última fecha de pago no válida |
1012 |
InvalidCreditLimit |
Límite de crédito inválido |
1013 |
InvalidInvAddrBranch |
Dirección de sucursal inválida |
1014 |
InvalidDiscountCode |
Código de descuento inválido |
1015 |
InvalidEDIInvoices |
Factura no válida para el intercambio electrónico de datos |
1016 |
InvalidEDIOrders |
Órdenes no válidas para el intercambio electrónico de datos |
1017 |
InvalidEDIReference |
Referencia no válida para el intercambio electrónico de datos |
1018 |
InvalidEDITransport |
Transporte no válido para el intercambio electrónico de datos |
1019 |
InvalidEDIAddress |
Dirección no válida para el intercambio electrónico de datos |
1020 |
InvalidEDIServerUser |
Usuario del servidor no válido para el intercambio electrónico de datos |
1021 |
InvalidEDIServerPassword |
Contraseña del servidor no válida para el intercambio electrónico de datos |
1022 |
InvalidTaxRef |
Referencia Fiscal inválida |
1023 |
InvalidCustomerPOLine |
Línea de pedido de compra no válida |
1024 |
DatabaseUpdateFailed |
Error al actualizar la base de datos |
1025 |
NoDebtorNumber |
Sin número de deudor |
1026 |
DebtorDoesntExist |
El deudor no existe |
1027 |
IncorrectBranchNumberLength |
Longitud incorrecta del número de sucursal |
1028 |
BranchNoAlreadyExists |
El número de la sucursal ya no existe |
1029 |
IncorrectBranchNameLength |
Nombre de la sucursal incorrecto |
1030 |
InvalidEstDeliveryDays |
Número de días estimados de entrega inválidos |
1031 |
AreaCodeNotSetup |
Código de área no configurado |
1032 |
SalesmanCodeNotSetup |
Código de vendedor no configurado |
1033 |
InvalidFwdDate |
Fecha inválida de envío |
1034 |
InvalidPhoneNumber |
Número telefónico inválido |
1035 |
InvalidFaxNumber |
Número de fax inválido |
1036 |
InvalidContactName |
Nombre del contacto inválido |
1037 |
InvalidEmailAddress |
Dirección de Correo Electrónico inválido |
1038 |
LocationCodeNotSetup |
El código de locación no configurado |
1039 |
TaxGroupIdNotSetup |
Grupo de impuesto no configurado |
1040 |
ShipperNotSetup |
Transportista no configurado |
1041 |
InvalidDeliverBlind |
Entrega no válida |
1042 |
InvalidDisableTrans |
Transporte inválido ó deshabilitado |
1043 |
InvalidSpecialInstructions |
Instrucciones especiales inválidas |
1044 |
InvalidCustBranchCode |
Código Inválido de sucursal del cliente |
1045 |
BranchNoDoesntExist |
Sucursal no existente |
1046 |
StockCodeDoesntExist |
El código de inventario no existe |
1047 |
StockCategoryDoesntExist |
Categoría de inventario no existe |
1048 |
IncorrectStockDescriptionLength |
La longitud de la descripción de inventario es incorrecta |
1049 |
IncorrectUnitsLength |
La longitud de las unidades es incorrecta |
1050 |
IncorrectMBFlag |
Marca incorrecta corredor mercantil |
1051 |
InvalidCurCostDate |
Fecha de costo actual inválido |
1052 |
InvalidActualCost |
Costo actual inválido |
1053 |
InvalidLowestLevel |
Nivel mínimo inválido |
1054 |
InvalidDiscontinued |
Inválido discontinuado |
1055 |
InvalidEOQ |
Cantidad económica de pedido inválida |
1056 |
InvalidVolume |
Volumen inválido |
1057 |
InvalidKgs |
Cantidad de kilogramos invalida |
1058 |
IncorrectBarCodeLength |
Longitud incorrecta del código de barras |
1059 |
IncorrectDiscountCategory |
Categoría de descuento incorrecta |
1060 |
TaxCategoriesDoesntExist |
Las categorías de impuesto no existen |
1061 |
InvalidSerialised |
Serializado inválido |
1062 |
IncorrectAppendFile |
Archivo adjunto incorrecto |
1063 |
InvalidPerishable |
Asignación de perecedero inválido |
1064 |
InvalidDecmalPlaces |
Cantidad de decimales inválidos |
1065 |
IncorrectLongStockDescriptionLength |
Longitud incorrecta de la descripción del archivo largo |
1066 |
StockCodeAlreadyExists |
Código de inventario ya existente |
1067 |
TransactionNumberAlreadyExists |
Número de transacción ya existente |
1068 |
InvalidTranDate |
Fecha de la transacción inválida |
1069 |
InvalidSettled |
Arreglo inválido |
1070 |
IncorrectReference |
Referencia incorrecta |
1071 |
IncorrectTpe |
Tipo Incorrecto |
1072 |
InvalidOrderNumbers |
Número de pedido inválido |
1073 |
InvalidExchangeRate |
Tasa de cambio inválido |
1074 |
InvalidOVAmount |
Cantidad OV inválida |
1075 |
InvalidOVGst |
Gst OV inválido |
1076 |
InvalidOVFreight |
Carga OV inválida |
1077 |
InvalidDiffOnExchange |
Diferecial cambiario inválido |
1078 |
InvalidAllocation |
Asignación invalida |
1079 |
IncorrectInvoiceText |
Texto de factura incorrecto |
1080 |
InvalidShipVia |
Embarque inválido |
1081 |
InvalidEdiSent |
Envío de intercambio electrónico de datos no válido |
1082 |
InvalidConsignment |
Envío no válido |
1083 |
InvalidLastCost |
Último costo no válido |
1084 |
InvalidMaterialCost |
Costo de material no válido |
1085 |
InvalidLabourCost |
Mano de obra no válida |
1086 |
InvalidOverheadCost |
Costo de gastos generales inválidos |
1087 |
InvalidCustomerRef |
Referencia de cliente inválida |
1088 |
InvalidBuyerName |
Nombre de comprador inválido |
1089 |
InvalidComments |
Comentarios inválidos |
1090 |
InvalidOrderDate |
Fecha de pedido inválida |
1091 |
InvalidDeliverTo |
Entrega inválida |
1092 |
InvalidFreightCost |
Gastos de flete no válidos |
1094 |
InvalidDeliveryDate |
Fecha de entrega no válida |
1095 |
InvalidQuotationFlag |
Bandera de oferta inválida |
1096 |
OrderHeaderNotSetup |
Encabezado de orden no configurado |
1097 |
InvalidUnitPrice |
Pecio unitario inválido |
1098 |
InvalidQuantity |
Cantidad inválida |
1099 |
InvalidDiscountPercent |
Porcentaje de descuento no válido |
1100 |
InvalidNarrative |
Narrativa inválida |
1101 |
InvalidItemDueDate |
Fecha de vencimiento de artículo no válido |
1102 |
InvalidPOLine |
Línea de orden de compra no válida |
1103 |
GLAccountCodeAlreadyExists |
El código de cuenta contable ya existe |
1104 |
IncorrectAccountNameLength |
La longitud del nombre de la cuenta es incorrecta |
1105 |
AccountGroupDoesntExist |
Grupo de cuentas no existe |
1106 |
GLAccountSectionAlreadyExists |
Sección de cuenta contable ya existe |
1107 |
IncorrectSectionNameLength |
La longitud del nombre de la sección es incorrecta |
1108 |
GLAccountGroupAlreadyExists |
Grupo de cuentas contable ya existe |
1109 |
GLAccountSectionDoesntExist |
Seccion de cuenta contable no existe |
1110 |
InvalidPandL |
PandL inválido |
1111 |
InvalidSequenceInTB |
Balance de comprobación de secuencia no válido |
1112 |
GLAccountGroupDoesntExist |
Grupo de cuenta contable no existe |
1113 |
InvalidLatitude |
Latitud inválida |
1114 |
InvalidLongitude |
Longitud inválida |
1115 |
CustomerTypeNotSetup |
Tipo de cliente no configurado |
1116 |
NoPricesSetup |
Precio no configurado |
1117 |
InvalidInvoicedQuantity |
Cantidad de factura no válida |
1118 |
InvalidActualDispatchDate |
Fecha de envío actual no válida |
1119 |
InvalidCompletedFlag |
Bandera completa no válida |
1120 |
InvalidCategoryID |
Identificación de categoría inválida |
1121 |
InvalidCategoryDescription |
Descripción de categoría inválida |
1122 |
InvalidStockType |
Tipo de inventario inválido |
1123 |
GLAccountCodeDoesntExists |
Código de cuenta contable no existe |
1124 |
StockCategoryAlreadyExists |
Categoría de inventario si existe |
1125 |
SupplierNoAlreadyExists |
Proveedor ya no existe |
1126 |
IncorrectSupplierNameLength |
La longitud del nombre del proveedor es incorrecta |
1127 |
InvalidSupplierSinceDate |
Proveedor inválido desde fecha |
1128 |
InvalidBankAccount |
Cuenta bancaria no válida |
1129 |
InvalidBankReference |
Referencia bancaria no válida |
1130 |
InvalidBankPartics |
Partidas de banco inválida |
1131 |
InvalidRemittanceFlag |
Bandera de remesas inválida |
1132 |
FactorCompanyNotSetup |
Factor de la compañía no configurado |
1133 |
SupplierNoDoesntExists |
El proveedor no existe |
1134 |
InvalidSuppliersUOM |
El proveedor no válida para la unidad de medida |
1135 |
InvalidConversionFactor |
Factor de conversión no válido |
1136 |
InvalidSupplierDescription |
Descripción del proveedor inválida |
1137 |
InvalidLeadTime |
Tiempo de entrega no válido |
1138 |
InvalidPreferredFlag |
Bandera preferida no válida |
1139 |
StockSupplierLineDoesntExist |
Línea del proveedor de inventario no existe |
1140 |
InvalidRequiredByDate |
Fecha requerida inválida |
1141 |
InvalidStartDate |
Fecha de inicio no válida |
1142 |
InvalidCostIssued |
Costo emitido no válido |
1143 |
InvalidQuantityRequired |
Cantidad requerida no válida |
1144 |
InvalidQuantityReceived |
Cantidad recibida no válida |
1145 |
InvalidStandardCost |
Costo Estándar no válido |
1146 |
IncorrectSerialNumber |
Número de serie incorrecto |
1147 |
WorkOrderDoesntExist |
La orden de trabajo no existe |
1148 |
InvalidIssuedQuantity |
Cantidad emitida no válida |
1149 |
InvalidTransactionDate |
Fecha de transacción no válida |
1150 |
InvalidReceivedQuantity |
Cantidad recibida no válida |
1151 |
ItemNotControlled |
Artículo no controlado |
1152 |
ItemSerialised |
Artículo serializado |
1153 |
BatchNumberDoesntExist |
Número de lote no existe |
1154 |
BatchIsEmpty |
El lote está vacío |
1155 |
NoSuchArea |
No hay tal área |
1156 |
NoSuchSalesMan |
Ningún vendedor |
1157 |
NoCompanyRecord |
Sin registro de empresa |
1158 |
NoReadOrder |
Sin orden de lectura |
1159 |
NoReadOrderLines |
Sin líneas de orden de lectura |
1160 |
NoTaxProvince |
Provincia sin impuestos |
1161 |
TaxRatesFailed |
Tasas de impuestos fallidas |
1162 |
NoReadCustomerBranch |
No lee sucursal de cliente |
1163 |
NoReadItem |
No lee artículo |
1164 |
MustBeReceiptOrCreditNote |
Debe ser recibo o nota de crédito |
1165 |
NoTransactionToAllocate |
Sin transacción para asignar |
1166 |
Como puede ver el código de error 1 indica "Sin Autorización", que será devuelto el error si el nombre de usuario o la contraseña son incorrectos y también si el nombre de la base de datos que se utilizá en la llamada a la API no se ha especificado correctamente en el archivo de webERP/api/api_php.php en la variable:
$api_DatabaseName="weberpdemo";
Para detectar los errores creamos una variable de sesión para contener cualquier mensaje de error que ocurra, de modo que podamos mostrarlo al usuario. Entonces el código de inicialización en la parte superior de index.php se convierte en:
<?php
include "xmlrpc/lib/xmlrpc.inc";
$xmlrpc_internalencoding="UTF-8";
include "xmlrpc/lib/xmlrpcs.inc";
$_SESSION["Errors"] = array();
?>
Y luego en la parte inferior de la salida tenemos un bucle a la salida de estos errores:
foreach ($_SESSION["Errors"] as $Error) {
echo $Error;
}
Ahora sólo necesitamos capturar ese error. Necesitamos poner este código en la parte inferior de la función GetLocations () para que ahora lea:
if ($ReturnValue[0] == 0) {
return $ReturnValue[1];
} elseif ($ReturnValue[0] == 1) {
$_SESSION["Errors"][] = "Incorrect login/password credentials used";
}
Ahora ejecute el comando index.php de nuevo en su navegador y obtendra un resultado similar al siguiente:
Sólo debemos poner este código al fondo de nuestras otras funciones, y entonces todos podrán detectar este error.
Ahora bien, si ponemos la contraseña correcta en index.php debería funcionar como antes.
Ahora intente ingresar un código de inventario que no sepa"si existe y ver qué pasa. Ingresé un código de pieza llamado" incorrecto "y esto es lo que verá:
Esta no es una salida muy útil, así que necesitamos detectar este error. Una mirada rápida aquí muestra que el código de error 1047 es "StockCodeDoesntExist" y esto debe ser devuelto si el código que ingresamos es incorrecto. Por lo tanto, debemos capturar el error 1047 en la función GetStockQuantity (). El código al final de esta función ahora se convierte en:
} 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";
}
Así que ahora la función es verificar que el usuario/contraseña es correcto, y también verificar que el código de inventario sea correcto y proporcionar información útil en caso de cualquier problema. Podríamos continuar y comprobar si existen otros errores, pero esto debería ser suficientes por ahora.
Para ello necesitamos una función similar a la que utilizamos para extraer la matriz de códigos de ubicación. Aquí está el código completo:
functionLocationName($LocationCode) {
//Encodethe data items
$UserID = php_xmlrpc_encode("admin");
$Password = php_xmlrpc_encode("webERP");
$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/webERP/api/api_xml-rpc.php");
$Client->setDebug(0);
//Create a message object, containing the parameters and the function name
$Message = new xmlrpcmsg("webERP.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"];
}
}
La primera sección codifica los parámetros como XML. Los dos primeros parámetros son siempre la combinación de ID de usuario/contraseña, y para esta llamada de función necesitamos un tercer parámetro, que es el código de la ubicación del que se necesita el nombre. La segunda sección es idéntica a la función anterior y crea una instancia de la clase de cliente XML-RPC. En la tercera sección se crea una instancia de la clase de mensaje, con el primer parámetro que es el nombre completo de la función de la API que se llama, en este caso webERP.xmlrpc_GetLocationDetails, y luego el segundo parámetro es una matriz de los parámetros codificados, (código de ubicación, contraseña del usuario). Este mensaje se envía al servidor, y la respuesta decodifica en una matriz llamada $ReturnValue.
Como la última vez, el primer elemento de la matriz indica si la función tuvo éxito (un cero) o cualquier otro entero para un código de error. El segundo elemento es una matriz asociativa de detalles para esa ubicación. La clave de cada elemento es el nombre del campo para ese valor. En nuestro caso sólo queremos el nombre de la ubicación, por lo que devolveremos el elemento ["locationname"]. Si fuera el número de teléfono que nos interesaba, simplemente devolveríamos el elemento ["tel"].
Cambiando la línea en el HTML donde llenamos el cuadro desplegable para:
Echo <option value =“. $ LocationCode.">. LocationName ($ LocationCode). "</ Option>";
El nombre completo de la ubicación aparece en el menú desplegable de la lista, pero el valor devuelto por el formulario sigue siendo sólo el código
Todo lo que queda para completar nuestro cliente, es escribir un código de inventario en el cuadro de texto, enviar el formulario y devolver la cantidad de existencias para ese código en la ubicación elegida. Primero necesitamos insertar algún código PHP en el HTML para manejar el formulario que se envia:
if (isset($_POST["submit"])) {
echo "The quantity of " . $_POST["StockID"] . " at " . $_POST["location"] . " is : " . GetStockQuantity($_POST["StockID"], $_POST["location"]);
}
Como se puede ver esto llama a otra función de PHP - GetStockQuantity () - que recupera la cantidad de existencias para el artículo requerido en la ubicación requerida. Observando la referencia de la función API en el manual, la función API que necesitamos es webERP.xmlrpc_GetStockBalance. Sin embargo, esta vez hay una pequeña adición que necesitamos ya que esta función devuelve una matriz que contiene los saldos de inventario en todas las ubicaciones para el artículo de inventario determinado.
El código completo de la función PHP es:
function GetStockQuantity($StockID, $LocationCode) {
//Encode the data items
$UserID = php_xmlrpc_encode("admin");
$Password = php_xmlrpc_encode("webERP");
$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/webERP/api/api_xml-rpc.php");
$Client->setDebug(0);
//Create a message object, containing the parameters and the function name
$Message = new xmlrpcmsg("webERP.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"];
}
}
}
No voy a pasar por esto en detalles, ya que es casi lo mismo que las funciones anteriores. La última sección es la clave:
$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"];
}
}
}
Aquí el RPC devuelve una matriz de ubicaciones con las cantidades de inventario para cada ubicación, y filtra la ubicación que necesitamos.
Mostrando que hemos devuelto los números correctos.