Retail:Order Loading WebService
Introduction
The ticket/order loading api provides a webservice which can be used to create tickets in Openbravo.
URL/Request & Authentication
The orderloader api can be called using a http request with the following characteristics:
- Authentication is done using a user and password passed in as parameters (resp. user and password in the request. In production you should be using https and preferably also enhanced network checking/access/firewalls for additional security. It is quite important that the default organization for the user is correct and corresponds to the pos terminal for which tickets are send to the system.
- synchronizedProcessing: if you pass true for this parameter the orders will be processed right away. See the section on synchronized processing below.
- you should use the POST request method
- the system expects the POST content to be a json string. The format of the json is discussed below.
Example url:
Automated Tests - Example jsons
The orderloader api is automatically checked and tested in our continuous integration. The automated tests provide some example of json messages. See the files in this location.
General JSON aspects
- Numbers are send as json doubles
- Dates are formatted as XML Schema date times with timezone and milliseconds: "orderDate": "2015-11-16T08:48:56.073Z"
- On order header level you can send a timezone offset separately, all date-time properties are offset using that timezone offset (unit is minutes), for example:
"timezoneOffset": -60
- Unique ids are in the form of UUID's of length 32, for example: F4BBBD005EC34E58A1E2FFFD51FA0374
- property name in the json should have quotes also, for example: "currency": "EUR"
- in the json described below you will find examples on how you can reference products, business partners etc. in the json. In general you can use the value of the database id, the name or searchKey. For some entities you can use additional properties, for example for currency you can use the isoCode.
Synchronized processing
The orderloader api can be called in synchronized-processing-mode or in async-processing-mode. In the first case the orders are processed directly and any errors are directly returned.
The non-synchronized mode uses the import transaction process. This means that the message is only checked for mandatory fields and invalid references. If the message passes this validation then it is stored in a buffer for subsequent asynchronous processing by a separate process. Any errors in the processing phase are handled internally and are not reported back to the caller.
WebPOS by default uses non-synchronized mode. Using a [Retail:Configuration_Guide#Configuring_Web_POS_Import_Transaction_Process|openbravo property] WebPOS can be set to use synchronized mode.
There are several reasons to use the non-synchronized mode:
- the initial processing when doing the request is fast. The non-synchronized mode should be used when there are really high volumes (1000+ tickets/minute).
- it can be good to have tickets stored as quickly as possible in the backend, the backend is more secure, it is backed up and can be accessed easily by backoffice personal to check/solve issues.
Main Structure
The main structure of the json object is shown as below:
{ "messageId": "F4BBBD005EC34E58A1E2FFFD51FA0374", "posTerminal": "VBS-1", "channel": "External", "data": [ ... //orders ] }
Some notes:
- the messageId is optional but it makes sense to use it because if you send the same message twice Openbravo will register this and automatically ignore the second/subsequent message if the first one is processed already.
- the code of the pos terminal corresponds to the search key of the pos terminal. It is a mandatory value, and it is very important that it doesn't correspond to a terminal/channel touchpoint being used in a real device running the Web POS. "Reusing" a terminal that is being used for the POS may lead to corrupted data such as incorrect cashup information, or duplicated document numbers.
- the channel property needs to be there, always use the value External
- the data array contains the orders/tickets. The order/ticket format is described below.
Json: creating a complete order, shipped and paid
This json gives an example of creating a ticket with a minimal json. The json below only visualizes the order, it is assumed that this json is contained in the data property of the main structure (see previous section).
For a shipped and paid order, the order should include the order header, lines and also the complete payments.
{ "currency": "EUR", "step": "all", "grossAmount": 24.9, "netAmount": 20.58, "businessPartner": "VBS/C0001", "lines": [{ "product": "WVG/S0011", "qty": 1, "grossAmount": 24.9, "netAmount": 20.58, "taxAmount": 4.32, "taxLines": { "Entregas IVA 21%": { "rate": 21, "netAmount": 20.58, "taxAmount": 4.32 } } }], "payments": [{ "paidAmount": 24.9, "date": "2015-11-16T08:48:51.524Z", "kind": "OBPOS_payment.cash", "rate": "1", "isocode": "EUR" }], "taxes": { "Entregas IVA 21%": { "rate": 21, "netAmount": 20.58, "taxAmount": 4.32 } } }
Notes on the properties:
- step: by setting the value "all" the system will assume that the order is paid completely (or paid enough) on the caller's side, the payments are created on the server and the order is shipped.
- currency is mandatory, use the isoCode as the value
- step: denotes the order steps you want to have executed. For now an order is assumed to be shipped/paid in one step, so always send the value "all"
- grossAmount: is the total amount of the order including taxes
- netAmount: is the total amount of the order excluding taxes
- businessParnter: should be the database id, name or searchkey of the business partner. If not set then the default defined for the pos terminal is used
It is possible to set the orderId from the caller, pass in the "id" property with a UUID of length 32 on the order header level. This will ensure that the order gets that id. The same can be done on order line level.
Then the order consists of 3 arrays: lines, payments and taxes. Each are described separately:
Order Lines
The order line denotes the sale or return of a product. An order line has the following json structure:
{ "product": "WVG/S0011", "qty": 1, "grossAmount": 24.9, "netAmount": 20.58, "taxAmount": 4.32, "taxLines": { "Entregas IVA 21%": { "rate": 21, "netAmount": 20.58, "taxAmount": 4.32 } } }
Notes on the properties:
- product: the database id, name, search key or ean of the product can be used here
- qty: a double number is allowed here
- grossAmount: the total amount including taxes
- netAmount: the amount excluding taxes
- taxAmount: the tax Amount
In addition the following properties can be passed in (they are computed from the grossAmount/netAmount and qty if not send):
- priceIncludesTaxes: default is false
- netListPrice: net unit price from price list, is defaulted as netAmount/qty
- grossListPrice: gross unit price from price list, is defaulted as grossAmount/qty
- netPrice: net actual unit price, is defaulted as netAmount/qty
- grossPrice: gross actual unit price, is defaulted as grossAmount/qty
- discountedNet: is defaulted from netAmount
- discountedLinePrice: is defaulted from netPrice
- discountPercentage: defaulted as zero
- listPrice: default from netListPrice
- priceList: default from grossListPrice
- standardPrice: defaulted from listPrice
- lineGrossAmount: defaulted from grossAmount
- unitPrice: defaulted from netPrice
- net and gross: defaulted from resp. netAmount and grossAmount
Each orderline can have zero or more taxLines:
"taxLines": { "Entregas IVA 21%": { "rate": 21, "netAmount": 20.58, "taxAmount": 4.32 } }
The taxlines are a bit special in that they are not an array but a json object:
- The property name is the identification of the tax. The database id, name or tax search key can be used.
- The rate is a percentage, not mandatory, if not set then it is computed from taxAmount and netAmount
- netAmount: the amount over which tax is computed
- taxAmount: the computed tax amount from the netAmount
Payments
The payments are passed in on order header level as a json array:
"payments": [{ "paidAmount": 24.9, "date": "2015-11-16T08:48:51.524Z", "kind": "OBPOS_payment.cash", "rate": "1", "isocode": "EUR" }]
Some notes:
- the paid amount is the payment in the currency of the order header
- the rate is the currency rate from the order currency to the payment currency
- you can pass in the amount in the payment currency with the "origAmount" property, if you don't pass it then it will be computed from the rate and paidAmount
- the isocode is the currency of the payment
- the kind refers to the payment method, the eligible payment methods are defined by POS terminal
Order Header Taxes
The tax totals are repeated on the order header:
"taxes": { "Entregas IVA 21%": { "rate": 21, "netAmount": 20.58, "taxAmount": 4.32 } }
As with the tax in the order lines, the taxes is not an array but a json object:
- The property name is the identification of the tax. The database id, name or tax search key can be used.
- The rate is a percentage, not mandatory, if not set then it is computed from taxAmount and netAmount
- netAmount: the amount over which tax is computed
- taxAmount: the computed tax amount from the netAmount
Separate create, (Partial) pay, ship and update
Create ticket
The create ticket action can be initiated by sending in the order with the property "step" set to "create". This will create the ticket with any payments you send in.
Note, updating tickets is not supported by the Openbravo OrderLoader webservice. So the order should be complete when sending it to Openbravo.
Pay ticket
You can send in payments of a ticket by sending in the complete ticket, the payments array should then contain the new payments (not the already send in payments). These payments are added to the payments already registered in the system. In addition the "step" property should have the value: "pay".
In this mode, the order lines, the business partner and the tax information don't need to be send again, it is possible but they are not updated.
Ship ticket
As a last step one would want to ship the ticket.
To ship the ticket the "step" property should have the value: "ship".
The complete ticket will be shipped. In this mode the order lines should be send again. Note that the order lines are not updated in the system, so the orderlines should be the same as when they are created. The payments array does not need to be send.
Update ticket
This step is used to update some ticket property. To update the ticket the "step" property should have the value: "update".
Example jsons
The json we use in the automated tests contain examples of separate create, pay, ship json messages: json examples.
Improvements in step Ship and Update
In order not to send all the mandatory fields in the json to the external orderloader, only is necessary to set in the json the identifier and the necessary properties. With the orderId the retail api is invoked and all properties are set. This improvement only be done in the "ship" and "update"(new) steps
Another improvement that has been developed is to allow partial deliveries on the step ship.
Json: using Openbravo properties for order, orderline, payment, tax
The above description provides a json which is easily consumed/used by an external api. There are however many more properties which can be passed from the external system. All the properties as defined in the Openbravo entity model can be passed on different levels in the json. This section gives pointers to these properties.
The main difference with the external-api json is that references to other data (foreign keys) need to use the Openbravo database id encapsulated in its own json object. For example a foreign key to the business partner needs to have the following structure:
"businessPartner": { "id": "F4BBBD005EC34E58A1E2FFFD51FA0374" }
In the json you can use the following entity properties:
- Order entity properties for the order header, these are copied into the sales order header when it is created in the database.
- Shipment entity properties for the order header, these are copied into the shipment header when it is created in the database.
- OrderLine entity properties for the order lines, these are copied in the order line when it is created in the database.
- Shipment Line properties can be used in the order line json, these are copied into the shipment line when it is created.
- Promotion properties can be used in the promotions json array send in with the order line.
Return Order/Lines
A return can be send in, in 2 ways:
- set a property "isReturn" on the order header to true. The complete ticket is considered to be a return. The document type associated to the order will be the one for returns.
- send in a standard ticket but with negative quantity for the return. In this case, the standard sales order document type will be used.
It is important to mention that the quantities in the orderlines and header will be saved as they are specified (so negative lines will be saved as negative, and positive lines positive). The previous point only affects the document type used for the saved order.
Approvals
Openbravo Commerce supports approval settings for different kinds of client side user actions. Also the order loading webservice allows to send in approval information. The webservice does not do explicit approval checking, it is assumed that the client side will take care of this.
This is an example of the json which can be passed in to add an approval to the order, this should be passed in at order header level in the json (same level as lines/payments etc.):
"approvals": [{ "approvalType": "OBPOS_approval.setPrice", "userContact": "100", "created": 1447701780509 }]
Possible relevant values for approval type are:
- OBPOS_approval.discounts: Web POS Discretionary Discount Approval
- OBPOS_approval.opendrawer.cashup: Web POS Open Drawer approval Cash Up
- OBPOS_approval.setPrice: Web POS set Price approval
- OBPOS_approval.returns: Web POS Returns Approval
Response json
The Openbravo order loader webservice will send back a response. The response depends on if the request was handled synchronously or not.
All the responses have the same main structure:
{ "response": { "status": 0, ... } }
The status property indicates if an error occurred, zero means ok, -1 means error.
Synchronous Response
The synchronous response will return the complete ticket as it was created in the database. From this json you can for example retrieve the database id which you can use to retrieve the order later on.
{ "response": { "status": 0, "result": "0", "orders": [{ "currency": "102", "step": "all", "grossAmount": 24.9, "netAmount": 20.58, "businessPartner": "VBS\/C0001", "lines": [{ "product": { "id": "CEB0751EC82C426FA82FE8F55E47578D" }, "qty": 1, "grossAmount": 24.9, "netAmount": 20.58, "taxAmount": 4.32, "taxLines": { "5235D8E99A2749EFA17A5C92A52AEFC6": { "rate": 21, "netAmount": 20.58, "taxAmount": 4.32, "amount": 4.32, "net": 20.58 } }, "uOM": "100", "promotions": [], "promotionMessages": [], "promotionCandidates": [], "tax": "5235D8E99A2749EFA17A5C92A52AEFC6", "lineRate": 1.21, "priceIncludesTax": false, "netListPrice": 20.58, "grossListPrice": 24.9, "netPrice": 20.58, "grossPrice": 24.9, "discountedNet": 20.58, "discountedLinePrice": 20.58, "discountPercentage": 0, "listPrice": 20.58, "priceList": 24.9, "standardPrice": 20.58, "lineGrossAmount": 24.9, "unitPrice": 20.58, "net": 20.58, "gross": 24.9 }], "payments": [{ "paidAmount": 24.9, "date": "2015-11-16T08:48:51.524Z", "kind": "OBPOS_payment.cash", "rate": "1", "isocode": "102", "currency": "102", "amount": 24.9, "origAmount": 24.9, "paid": 24.9, "mulrate": "1" }], "taxes": { "5235D8E99A2749EFA17A5C92A52AEFC6": { "rate": 21, "netAmount": 20.58, "taxAmount": 4.32, "amount": 4.32, "net": 20.58 } }, "posTerminal": "9104513C2D0741D4850AE8493998A7C8", "payment": 24.9, "isQuotation": false, "isLayaway": false, "isReturn": false, "obposAppCashup": "-1", "created": 1449011593754, "approvals": [], "change": 0, "timezoneOffset": 0, "generateInvoice": false, "orderDate": "2015-12-02T00:13:13.013+0100", "creationDate": "2015-12-02T00:13:13.013+0100", "obposCreatedabsolute": "2015-12-02T00:13:13.013+0100", "organization": "D270A5AC50874F8BA67A88EE977F8E3B", "client": "39363B0921BB4293B48383844325E84C", "obposApplications": "9104513C2D0741D4850AE8493998A7C8", "id": "F45EB56E855C4E878818E8DCE29D52CF", "documentType": "511A9371A0F74195AA3F6D66C722729D", "bp": { "_identifier": "VBS Customer", "_entityName": "BusinessPartner", "$ref": "BusinessPartner\/ABD91C9D3BC94175B876FBBE9CACA008", "id": "ABD91C9D3BC94175B876FBBE9CACA008", "client": "39363B0921BB4293B48383844325E84C", "client$_identifier": "The White Valley Group", "organization": "D270A5AC50874F8BA67A88EE977F8E3B", "organization$_identifier": "Vall Blanca Store", "active": true, "creationDate": "2013-07-04T23:01:13+02:00", "searchKey": "VBS\/C0001", "name": "VBS Customer", "name2": null, "description": null, "summaryLevel": false, "businessPartnerCategory": "B1DD8B371643421C987EF1D5B358D5E9", "businessPartnerCategory$_identifier": "Customer", "oneTimeTransaction": false, "potentialCustomer": false, "vendor": false, "customer": true, "employee": false, "isSalesRepresentative": false, "referenceNo": null, "dUNS": null, "uRL": null, "language": "en_US", "language$_identifier": "English (USA)", "taxID": null, "taxExempt": false, "invoiceSchedule": null, "locId": "2AA7EADDF7EC405899262DDA3E572436" }, "warehouse": "A154EC30A296479BB078B0AFFD74CA22", "priceList": "496CF965DF9744D2A41248392D1DE407", "priceIncludesTax": true, "documentNo": "VBS1000001", "orderType": 0, "gross": 24.9, "net": 20.58 }] } }
Asynchronous Response
When an asynchronous request is done a simpler json is returned. The ticket is created asynchronously in the database so it is not possible for the system to return the order information. Only a success or failure (see next section) json is returned with user information:
{ "response": { "status": 0, "result": "0", "contextInfo": { "userId": "3073EDF96A3C42CC86C7069E379522D2", "roleId": "5FA11B3DD8F04C0986C774624809C31E", "orgId": "D270A5AC50874F8BA67A88EE977F8E3B", "clientId": "39363B0921BB4293B48383844325E84C" } } }
Error Response
An error is indicated by the status property which will have value -1. The error message can be be found in the message property of the error object.
{ "response": { "status": -1, "error": { "message": "Value OBPOS_payment.cash.DOESNOTEXIST does not resolve to a payment type for terminal VBS-1", "messageType": "Error", "title": "" }, "totalRows": 0 } }
Retrieving an order
Orders can be retrieved using the JSON REST webservice api. The authentication for the json webservice see this part of the wiki.
For example to retrieve a specific order header you can use the following request:
In your specific situation you have to replace the hostname, port, context and id (the last part of the url) with the order you want to retrieve.
This request will return a result like this:
{ "_identifier": "VBS1000029 - 02-12-2015 - 24.90", "_entityName": "Order", "$ref": "Order\/6C06A593D9B442F585938A58B1CE79DA", "id": "6C06A593D9B442F585938A58B1CE79DA", "client": "39363B0921BB4293B48383844325E84C", "client$_identifier": "The White Valley Group", "organization": "D270A5AC50874F8BA67A88EE977F8E3B", "organization$_identifier": "Vall Blanca Store", "active": true, "creationDate": "2015-12-02T08:17:36+01:00", "createdBy": "3073EDF96A3C42CC86C7069E379522D2", "createdBy$_identifier": "Vall Blanca Store User", "updated": "2015-12-02T08:17:37+01:00", "updatedBy": "3073EDF96A3C42CC86C7069E379522D2", "updatedBy$_identifier": "Vall Blanca Store User", "salesTransaction": true, "documentNo": "VBS1000029", "documentStatus": "CO", "documentAction": "--", "processNow": false, "processed": true, "documentType": "511A9371A0F74195AA3F6D66C722729D", "documentType$_identifier": "VBS POS Order", "transactionDocument": "511A9371A0F74195AA3F6D66C722729D", "transactionDocument$_identifier": "VBS POS Order", "description": null, "delivered": false, "reinvoice": false, "print": false, "selected": false, "salesRepresentative": null, "orderDate": "2015-12-02", "scheduledDeliveryDate": "2015-12-02", "datePrinted": null, "accountingDate": "2015-12-02", "businessPartner": "ABD91C9D3BC94175B876FBBE9CACA008", "businessPartner$_identifier": "VBS Customer", "invoiceAddress": "2AA7EADDF7EC405899262DDA3E572436", "invoiceAddress$_identifier": ".Barcelona, Pau Claris 138 1-1", "partnerAddress": "2AA7EADDF7EC405899262DDA3E572436", "partnerAddress$_identifier": ".Barcelona, Pau Claris 138 1-1", "orderReference": null, "printDiscount": false, "currency": "102", "currency$_identifier": "EUR", "formOfPayment": "B", "paymentTerms": "D8955F8808A54C63BBF478A6843D834D", "paymentTerms$_identifier": "Treinta días", "invoiceTerms": "I", "deliveryTerms": "A", "freightCostRule": "I", "freightAmount": 0, "deliveryMethod": "P", "shippingCompany": null, "charge": null, "chargeAmount": 0, "priority": "5", "summedLineAmount": 20.58, "grandTotalAmount": 24.9, "warehouse": "A154EC30A296479BB078B0AFFD74CA22", "warehouse$_identifier": "Vall Blanca Store Warehouse", "priceList": "496CF965DF9744D2A41248392D1DE407", "priceList$_identifier": "The White Valley Group Sale Price List", "priceIncludesTax": true, "salesCampaign": null, "project": null, "activity": null, "posted": "N", "userContact": "032576220FC44B0DA80145B3265E4D9A", "userContact$_identifier": "VBS Customer", "copyFrom": false, "dropShipPartner": null, "dropShipLocation": null, "dropShipContact": null, "selfService": false, "trxOrganization": null, "stDimension": null, "ndDimension": null, "deliveryNotes": null, "incoterms": null, "iNCOTERMSDescription": null, "generateTemplate": false, "deliveryLocation": null, "copyFromPO": false, "paymentMethod": "45A202BF44884F05B8A1BF741E2063B6", "paymentMethod$_identifier": "Cash", "fINPaymentPriority": null, "pickFromShipment": false, "receiveMaterials": false, "createInvoice": false, "returnReason": null, "addOrphanLine": false, "asset": null, "oBSCNTRBillingGroup": null, "calculatePromotions": false, "oBSCNTRProcessorder": "CO", "costcenter": null, "createOrder": false, "rejectReason": null, "validUntil": null, "quotation": null, "reservationStatus": null, "createPOLines": false, "obdiscAddpack": false, "cashVAT": false, "pickfromreceipt": false, "aPRMAddPayment": false, "obposApplications": "9104513C2D0741D4850AE8493998A7C8", "obposApplications$_identifier": "VBS POS Terminal", "obposAppCashup": "-1", "obposSendemail": false, "obposEmailStatus": null, "obposCreatedabsolute": "2015-12-02T09:17:36+01:00", "obposRejectedQuotation": null, "deliveryStatus": 100, "oBPOSNotInvoiceOnCashUp": false, "invoiceStatus": 0, "paymentStatus": 100, "_computedColumns": "6C06A593D9B442F585938A58B1CE79DA", "_computedColumns$_identifier": "Order_ComputedColumns (6C06A593D9B442F585938A58B1CE79DA)", "recordTime": 1449044844033 }
The properties are described in the Order entity model.
To retrieve the last 100 orders of a specific pos terminal you can use a url like the following:
This returns a result like this (truncated for clarity):
{ "response": { "data": [ //orders as json objects ], "status": 0, "totalRows": 51, "startRow": 0, "endRow": 50 } }
So a response json with a data json array property.
To retrieve the order lines of an order, use the order id to filter:
{ "response": { "data": [{ "_identifier": "VBS1000029 - 02-12-2015 - 24.90 - 10 - 20.58", "_entityName": "OrderLine", "$ref": "OrderLine\/610CAAC1E0F4440B855433E2EE265BF4", "id": "610CAAC1E0F4440B855433E2EE265BF4", "client": "39363B0921BB4293B48383844325E84C", "client$_identifier": "The White Valley Group", "organization": "D270A5AC50874F8BA67A88EE977F8E3B", "organization$_identifier": "Vall Blanca Store", "active": true, "creationDate": "2015-12-02T08:17:37+01:00", "createdBy": "3073EDF96A3C42CC86C7069E379522D2", "createdBy$_identifier": "Vall Blanca Store User", "updated": "2015-12-02T08:17:37+01:00", "updatedBy": "3073EDF96A3C42CC86C7069E379522D2", "updatedBy$_identifier": "Vall Blanca Store User", "salesOrder": "6C06A593D9B442F585938A58B1CE79DA", "salesOrder$_identifier": "VBS1000029 - 02-12-2015 - 24.90", "lineNo": 10, "businessPartner": "ABD91C9D3BC94175B876FBBE9CACA008", "businessPartner$_identifier": "VBS Customer", "partnerAddress": null, "orderDate": "2015-12-02", "scheduledDeliveryDate": null, "dateDelivered": null, "invoiceDate": null, "description": null, "product": "CEB0751EC82C426FA82FE8F55E47578D", "product$_identifier": "Ski skin", "warehouse": "A154EC30A296479BB078B0AFFD74CA22", "warehouse$_identifier": "Vall Blanca Store Warehouse", "directShipment": false, "uOM": "100", "uOM$_identifier": "Unit", "orderedQuantity": 1, "reservedQuantity": 0, "deliveredQuantity": 1, "invoicedQuantity": 0, "shippingCompany": null, "currency": "102", "currency$_identifier": "EUR", "listPrice": 20.58, "unitPrice": 20.58, "priceLimit": 0, "lineNetAmount": 20.58, "discount": 0, "freightAmount": 0, "charge": null, "chargeAmount": 0, "tax": "5235D8E99A2749EFA17A5C92A52AEFC6", "tax$_identifier": "Entregas IVA 21%", "resourceAssignment": null, "sOPOReference": null, "attributeSetValue": null, "descriptionOnly": false, "orderQuantity": null, "orderUOM": null, "priceAdjustment": null, "standardPrice": 20.58, "cancelPriceAdjustment": false, "orderDiscount": null, "editLineAmount": false, "taxableAmount": null, "goodsShipmentLine": null, "returnReason": null, "grossUnitPrice": 0, "lineGrossAmount": 24.9, "grossListPrice": 24.9, "costcenter": null, "baseGrossUnitPrice": 0, "asset": null, "warehouseRule": null, "stDimension": null, "quotationLine": null, "ndDimension": null, "createReservation": null, "project": null, "reservationStatus": null, "manageReservation": false, "managePrereservation": false, "explode": false, "bOMParent": null, "recordTime": 1449045677743 }], "status": 0, "totalRows": 1, "startRow": 0, "endRow": 0 } }
The properties are described in the OrderLine entity model.
Extending the Order Loader with your custom data resolving logic
In case your situation requires to override the default data resolving logic of the order loader then you can implement a so-called DataResolver (available from 16Q4). The data resolver is called before the default order loader resolves references to customers, products, etc. If your logic resolves/finds the referenced data then the default logic will not be used.
See below an example of a data resolver. The only thing you need to do is create a module and develop a class which implements the DataResolver interface as is shown below. At runtime the Openbravo system will automatically find your data resolver and use it.
package org.openbravo.multiupc; import org.openbravo.dal.core.OBContext; import org.openbravo.dal.service.OBDal; import org.openbravo.dal.service.OBQuery; import org.openbravo.model.common.plm.Product; import org.openbravo.retail.posterminal.ExternalOrderLoader; /** * Example of DataResolver */ public class MultiUPCDataResolver implements ExternalOrderLoader.DataResolver { @Override public String resolveJsonValue(String entityName, String searchValue, String... properties) { if (!Product.ENTITY_NAME.equals(entityName)) { return null; } OBContext.setAdminMode(); try { final OBQuery<MultiUPC> qry = OBDal.getInstance().createQuery(MultiUPC.class, MultiUPC.PROPERTY_UPC + "=:upc"); qry.setNamedParameter("upc", searchValue); for (MultiUPC upc : qry.list()) { return upc.getProduct().getId(); } } finally { OBContext.restorePreviousMode(); } return null; } }
Note:
- your data resolver is called to resolve all the references. So you should in resolveJsonValue method if the call is being done for an entity you can resolve. See the example above.
- if there are multiple data resolver defined in the system then currently the order in which they are processed is defined by their priority. The default Openbravo data resolver has a priority of a 100.
If you set a dataloader with a priority less than 100 then it will be executed before. If there are dataloaders with the same priority then
Handling duplicate messages
The external order loader is capable of handling duplicate order loading requests. So messages which are send multiple times because the caller for example thinks that the first request did not arrive.
To handle this situation the response of an external order loader request is stored in the database.
To detect duplicate messages it is important that you pass a messageId property in the json in the top-level as is shown below. If you then repeat a message use the same message id as the previous request.
{ "messageId": "ABC1234", "posTerminal": "VBS-1", "channel": "External", "data": [{ "id": "DEF12345", "currency": "EUR", "step": "all", "grossAmount": 24.9, "netAmount": 20.58, "businessPartner": "VBS/C0001", "lines": [{ "id": "$line0Id", "product": "WVG/S0011", "qty": 1, "grossAmount": 24.9, "netAmount": 20.58, "taxAmount": 4.32, "taxLines": { "Entregas IVA 21%": { "rate": 21, "netAmount": 20.58, "taxAmount": 4.32 } } }], "payments": [{ "paidAmount": 24.9, "date": "2015-11-16T08:48:51.524Z", "kind": "OBPOS_payment.cash", "rate": "1", "isocode": "EUR" }], "taxes": { "Entregas IVA 21%": { "rate": 21, "netAmount": 20.58, "taxAmount": 4.32 } } }] }
When handling duplicate requests there can be different situations:
- the previous message was already processed or resulted in an error: in this case the previous response is returned (this also returns the error response).
- the previous message is still being processed: the logic will wait a maximum time to give the system time to process it. The default wait time is 10 seconds but can be overriden with the preference: 'External Order Loader Wait for Processing (seconds)'. If after waiting the message is not yet processed an error is returned. If the message has been processed then the response stored in the db is returned.