Interface has two methods:
operation
which gets called with an instance of a class derived fromBillsHandlerRequest
, depending on the POS event:BillClosingRequest
when bill is closed (printed)BillReOpeningRequest
when bill is re-openedBillRePrintingRequest
when bill is re-printedBillProformaRequest
when proforma is printedBillCancelArticlesRequest
when void unpaid orderBillPaymentByCodeRequest
when payment by codeBillPaymentCancelledRequest
when user exits from payment screen and cancels payments which he has already chosenBillAddingOrderRequest
when order added to bill
supportedRequests
default method witch returns set of supported requests. By default almost all requests are supported for backward compatibility. SeeJavadoc
. It is possible to change number of supported requests based on driver instance configuration
public class VariableQuantitySupportedRequestsBillsHandler implements IBillsHandler {
@Override
public BillsHandlerResult operation(DriverConfiguration cfg, BillsHandlerRequest request) {
//Sophisticated code goes here
return null;
}
@Override
public Set<Class<? extends BillsHandlerRequest>> supportedRequests(DriverConfiguration cfg) {
Set<Class<? extends BillsHandlerRequest>> requests = new HashSet<>();
requests.add(BillPaymentCancelledRequest.class);
if ("true".equals(cfg.getParams().get("SOME_DRIVER_PARAM"))) {
requests.add(BillCancelArticlesRequest.class);
}
return requests;
}
}
Every request contains bill structure: items, payments, etc. For any of operations above method should return instance of BillsHandlerResult
, or null.
Bills handling drivers support asynchronous error handling, for the cases when operation in case of failure should not block POS flow. To handle error asynchronously, Bills Handling driver may throw one of two exceptions:
EBillErrorAsyncResolution
- when this error thrown, POS continues normal flow, in one minute the same driver method is called again with the same arguments, in case of same failure error show in in POS allowing user to choose what to do:- Retry - another attempt of the same operation
- Ignore - cancel this job, do not make more attempts of this operation
EBillErrorAsyncRetry
- POS continues normal flow, in one minute the same driver method is called again with the same arguments.
public class MyBillsHandler implements IBillsHandler {
// ...
@Override
public BillsHandlerResult operation(DriverConfiguration cfg, BillsHandlerRequest request) {
if (request instanceof BillClosingRequest) {
try {
getConnection(cfg).sendStatistics(request.getBill());
return new BillsHandlerResult(); // Return empty result
} catch (IOException e) {
log.error("Error sending statistics", e);
throw new EBillErrorAsyncResolution(e);
}
}
return null;
}
}
There are also two operations which may be useful for creating integrations with loyalty systems:
By implementing this operation your driver, depending on the receipt structure, may register unTill payment and/or apply discounts. Normally this operation is used for using with customer and loyalty cards, vouchers, coupons etc.
This request sent when unTill(r) action Request Payment by Code
executes in POS Payment Screen and user types or scans some code in popup window. In addition to the bill structure request provides a typed code. A result of this operation is an instance of BillPaymentByCodeResult
, or exception.
BillPaymentByCodeResult
structure:
- paymentId - ID of unTill(r) payment mode to register payment. When not specified, result is ignored. Normally the payment ID may be configured as a parameter in (Driver Configuration Declaraion;
- data - transaction data, key/value map to store transaction details for further usage and/or display on bill receipt ticket (e.g. unique transaction id, etc);
- amount - paid amount, may be less then the receipt total (partial payment). May be zero if no payment is done;
- discountItems - list of discounts to be applied in current receipt (see below);
- displayText - text message to show in POS (optional).
Note that even when payment amount is zero, a payment will be registered in unTill with zero amount, because the transaction data is linked to payment.
Your driver may add discounts to be applied in current receipt. This may be achieved by adding instances of a classes derived from BillDiscountItem
to discountItems collection in BillPaymentByCodeResult
object:
BillDiscountByVatItem
- adds discount per VAT%. unTill display one line per VAT% in receipt, in "Articles" section. This way is useful when you don't know to which exactly item in receipt it is applied. An instance of this class provides:vatPercent
- VAT%, (e.g. "20.00") to which discount is applied;vatSign
(optional)discountKey
- this is a key for the discount item, e.g. "DiscountVat20%". Must be uniquer per VAT%.discountTitle
- title to display discount item in receipt. Note that when you use the discount for the first time, unTill(r) creates an special article for this discount. A name in backoffice will be set todiscountKey
and article's "alias" set todiscountTitle
. This alias is used on receipts can be changed in backoffice.discountDescription
(optional)
BillDiscountOnOrderItem
- adds discount per top-level item in receipt. Driver receives receipt inBillPaymentByCodeRequest.bill.orderItems
collection. For every item there is asignature
field which is unique per receipt. All items with non-empty signature are top-level items. If any top-level item has modifiers, they are listed in the same collection after the top-level item with empty signature. It is important to understand thatBillDiscountOnOrderItem
represents a discount per top-level item. It means that if driver returnsBillDiscountOnOrderItem
with some amount, this discount amount will be applied in unTill to a top-level item and it's modifiers. An instance of this class provides:orderItemSignature
- a signature of top-level receipt line;discountReason
- a discount reason, may be configured as a parameter in (Driver Configuration DeclaraiondiscountDescription
(optional)
This request sent when a payment process has been cancelled and there were added payments for this receipt already. If your driver has added any payment with BillPaymentByCodeResult
, it has to revert them when this request received. A result is an instance of BillsHandlerResult
:
@Override
public BillsHandlerResult operation(DriverConfiguration cfg, BillsHandlerRequest r) {
if (request instanceof BillPaymentByCodeRequest) {
return request(cfg, (BillPaymentByCodeRequest)r);
} else if (request instanceof BillPaymentCancelledRequest
|| request instanceof BillReOpeningRequest) {
return cancel(cfg, r.getBill().getPaymentItems());
}
return null;
}
private void cancel(DriverConfiguration cfg, List<PaymentItem> paymentItems) {
Long myPaymentModeId = new Long(cfg.getParams().get(MyConst.PARAM_PAYMENT_MODE_ID));
for (PaymentItem pi : paymentItems)
if (myPaymentModeId.longValue == pi.getPaymentId())
revertTransaction(cfg, pi.getData().get(MyConst.DATA_TRX_ID));
return new BillsHandlerResult();
}
private void revertTransaction(DriverConfiguration cfg, String trxId) {
if (trxId != null && !"".equals(trxId)) {
// TODO: implement reverting transaction
}
}
If your driver has added any payment with BillPaymentByCodeResult
, it is important to decide what your driver does when such bill is re-opened. In case of successful re-open unTill discards all payments and driver discounts (discounts added by BillPaymentByCodeResult
). Normally there are three possible approaches to handle BillReOpeningRequest
:
- Return null or instance of
BillsHandlerResult
. In this case driver does simply nothing, and bill successfully re-opened in POS. - Revert every payment of the bill which seems to be "your" payment, e.g. added by your driver. This is shown in example above. In this case original transaction(s) will be reverted, which is the most correct way to handle this operation.
- Throw an exception. This may be useful if you don't allow re-opening bills with payments registered by your driver. It would be wise in this case to check if bill actually contains "your" payments before throwing exception.