calculateShoppingCart

Calculate the value of a shopping cart, and have discounts, promotions and taxes automatically calculated by API. You only need to input item IDs and quantities.

calculateShoppingCart is a very versatile function that:

  • For each item, finds the sales price that should apply in given location, and to the specified customer.
  • Automatically applies price list prices, discounts and promotion rules.
  • Automatically applies correct tax rate to each item.
  • Calculates invoice net total, tax/VAT and total, using the same algorithm as Erply backend.
You need to input product IDs and quantities, and you may also specify location ID and customer ID.

calculateShoppingCart can be used in web shops, or to easily implement a custom Point of Sale application on top of Erply API. This API call is used by Erply's own Touch POS, so the most up-to-date implementation is guaranteed.

Price list and promotion rules are very complicated and implementing them locally would also mean you need to download huge amounts of data. That is why we recommend to call calculateShoppingCart instead.

Input parameters

Parameter name Description Possible value Required
documentID Sales document ID. integer
type Possible values: INVWAYBILL, CASHINVOICE, WAYBILL, PREPAYMENT, OFFER, EXPORTINVOICE, RESERVATION, ORDER, INVOICE, CREDITINVOICE.

Default value is CASHINVOICE.
string
date If omitted, current date will be used. ISO date (yyyy-mm-dd)
warehouseID Warehouse ID.

You may specify either warehouseID or pointOfSaleID, whichever is more convenient for you.

If you do not specify neither location nor register, API automatically assumes you are selling from the first location in your locations list (by ID). All price lists and promotions associated with that location will apply, and that location's tax rate will be set to all items.

To be always sure of what location's price lists, promotions and taxes are being applied, we recommend to set this parameter explicitly.
integer
pointOfSaleID Register ID.

An alternative to warehouseID.
integer
pricelistID Price list ID.

Set this value ONLY when you want to apply just this one price list to the sale and no others. In most cases, you do not need that! API will automatically apply any store, customer, or customer group price lists that have been defined, and will combine them automatically.
integer
customerID Customer ID.

If not set, API will not make any assumptions about the customer, so no customer (or customer group) price lists will apply. If you want the sale to go to the "Default customer" or "POS Customer", send that customer's ID.
integer
currencyRate

Exchange rate of the shopping cart currency against system's default currency. If the shopping cart is in system's default currency, this parameter does not have to be specified (but if you specify it, use 1.0 as the value).

Pay attention to the way rates should be divided. If your shopping cart is in JPY and your account default currency is EUR, the expected value is 0.008 (1 JPY = 0.008 EUR), not 125.

To get the current rate for a currency defined in Erply back office, see getCurrencies.

When using currencies, Erply can only apply price list prices, not any promotions.. Translating a promotion into a foreign currency has not been implemented yet. So, if you want to use the "currencyRate" input parameter, you must also specify "doNotApplyPromotions" = 1. If this parameter is specified without "doNotApplyPromotions" flag, API returns error 1010.

If currencyRate is not a valid positive decimal, API returns error 1014.

Decimal
applyQuotedPrices

API calculateShoppingCart can also apply "a price from customer's previous quote". We assume that if a confirmed quote has been given to a customer (and it has not expired yet), then the items on the quote could be freely sold at the indicated price, as many times as the customer wants. If you want this feature, set this flag to 1.

If this flag has been set to 1, and there is a quote for the specified customer that has not expired yet, and it contains the specific product, the price from the quote will override price list price (even if quoted price is higher than price list price). Promotions, however, will still apply on top of the quote price as usual.

Currently, this flag only has an effect if you have not disabled the use of quote prices with configuration parameter "disable_automatic_price_from_previous_quote" = 1. This limitation might be removed in the future.

Integer (1 or 0)
doNotApplyPromotions If set to 1, then only price lists will be applied — not promotions. Integer (1 or 0)
getAutomaticCoupons If set to 1, API will also return a list of coupons that should be automatically printed from POS after the sale. See output field automaticCoupons for a more detailed explanation. We recommend to leave it unset while you are still building up shopping cart contents — but set it to 1 when the cart has been finalized and you issue the final call to calculateShoppingCart. integer
couponIdentifiers Eg: "109000002346,109000002351"
Coupon codes (identifiers) that cashier has scanned.

Erply allows you to issue printed coupons to customers. Each printed coupon must have a unique identifier. In Erply backend, all issued coupons are listed in the Retail Chain » Issued Coupons module. A coupon is basically a method for invoking a sales promotion, so if customer returns to the store with a coupon and cashier scans it, a promotion will apply.

The recommended API workflow for accepting coupons is as follows:
  1. Use verifyIssuedCoupon to make sure that the coupon identifier exists, that the coupon has not expired and has not been redeemed yet.
  2. If coupon is valid, pass its identifier into calculateShoppingCart, field couponIdentifiers, so that it would give appropriate discounts. If the coupon has any effect to the sale, API will return its code in the usedCouponIdentifiers field. (Because it may well be that the coupon is not valid in given store, or customer has not chosen to buy the items required for the promotion to kick in.)
  3. If API has indicated that the coupon was indeed used, redeem it. After saving the sale with saveSalesDocument, call redeemIssuedCoupon to redeem the coupon. A redeemed coupon cannot be used again.

NB! Coupons are not the same as "promotional codes", eg. "Get 20% off of all barbecue equipment with promo code JULY4" (which many be printed in an ad, or distributed in customer newsletter). As far as Erply is concerned, you do not need a coupon setup for that.

For that case, we recommend to define a manual promotion, set "JULY4" as its name, and have cashier to apply the promotion manually whenever customer presents the code.

Certain types of promotions allow to scan more than one coupon (the promotion will then apply multiple times):
  • Buy a specific product and get $ off entire invoice
  • Buy for at least $x and get $ off entire invoice
  • Buy for at least $x and get $ off specific items
  • Buy for at least $x and get % off specific items
String (Comma-separated strings)
manualPromotionIDs Eg: "1,4,18"
Old deprecated name: "manualCampaignIDs"

IDs of promotions that cashier has applied manually.

In Erply, a promotion can be configured in three ways:
  1. Promotion applies automatically to all customers whenever possible (default option). API calculateShoppingCart applies all such promotions, you do not need to specify anything.
  2. Promotion can be applied manually at cashier's discretion. POS would presumably list all promotions that are applicable in that way, and cashier can invoke any of these. Pass the IDs of manually selected promotions in field manualPromotionIDs.
  3. Promotion applies when customer presents a coupon. Send the coupon identifiers in field couponIdentifiers (see specification above) and API will apply respective promotions if possible.
The promotion IDs sent in the manualPromotionIDs field must all be manual promotions; others will be ignored.

A promotion MAY be listed multiple times (if you want to apply it multiple times), but this only works with the following promotion types:
  • Buy a specific product and get $ off entire invoice
  • Buy for at least $x and get $ off entire invoice
  • Buy for at least $x and get $ off specific items
  • Buy for at least $x and get % off specific items
Also, multiple invocation does not work with one-time promotions of one-time birthday promotions.
String (Comma-separated integers)
doNotApplyInvoiceLevelPromotions Integer (0 or 1)
taxExempt Set a manual tax exemption to this sale.

If the selected customer is already marked as being tax exempt on customer card, you do not need to send this flag.

If customer provides a tax exemption certificate number, you may attach it to the sale using the taxExemptCertificateNumber field in saveSalesDocument.
Integer (0 or 1)
euInvoiceType Whether the sale is a domestic sale, a sale to another EU country or a sale to outside of the EU. Possible values are "DOMESTIC", "EU", "OUTSIDE_EU". The default tax rate suggested by API calculateShoppingCart depends on that.

If this concept does not apply (your business is not in the EU), just leave the field unset.
string
***** Send invoice lines (ie., items and quantities) as a flat list. For each item, you may send the following input parameters. Replace # with a sequential number (0, 1, 2, 3 etc.)
productID# ID of the product (SKU) sold. integer yes
amount# Sold quantity.

In general, quantity can be unlimited. However, for performance reasons, API imposes two limitations.

  1. If row quantity is 5000 or larger, no item-level promotions will be applied to this row, and this row will not trigger any promotions, either. (Neither "Buy this item and get discount on another product" nor "Buy this item and get discount from the whole purchase" promotions will be triggered.) Supporting these operations with larger quantities would require a lot of memory and time. Furthermore, if the "calculateShoppingCart" call is coming from POS, a large quantity is usually a cashier's typing error (product code been scanned into the "Quantity" field, for example), rather than a legitimate use case.
  2. Even if row quantity is below 5000, each item-level promotion will be applied at most 1000 times. If you define a "Buy One, Get One" promotion and attempt to sell 4000 items, calculateShoppingCart will apply the discount to only 1000 items (instead of expected 2000, or every other item).

number yes
price# Net price.

Set this field ONLY if cashier has entered a price manually. In other cases, do NOT send this input field. API will itself find and set correct item price.

This parameter will overwrite product's price list price (or product card price, if no price list applies). On top of this price, API will apply promotion discounts and a manual discount.
Decimal
discount# Manual discount.

Set this field ONLY if cashier has set a manual discount for this invoice line. Manual discount will be applied cumulatively on top of promotion discounts; there is no way to "override" a promotional discount with a manual discount.

In general, price lists, discounts and promotions are applied by the API in the following order:
  1. Price lists. Several price lists may apply to both the location and the customer, and price lists may overwrite each other in various ways.

    If the resulting price is different from product card price, calculateShoppingCart does not show or interpret it as a discount. So, if product card price is $10 and price list price is $5, API will return that item price is just $5. It will not be shown as a 50% discount.

    If you have specified the "price" parameter, it overwrites price list price.
  2. Manual discount. If you have specified a discount percentage, it will be further subtracted from the final price.
  3. Promotions.
Decimal
vatrateID# Set this field if you want a specific VAT / tax rate to be applied to this item. If not set, API will automatically determine the appropriate tax rate (which may be set at register, location, product group or product level). For a list of tax rates and IDs, see getVatRates.

For reference, this is the priority (in decreasing order) of various tax rate settings that you can specify in Erply backend:
  1. Tax exemption is activated at POS (set input field taxExempt = 1), or customer has been defined as tax exempt
  2. Product defined as "tax free in all locations" (on product card)
  3. Tax rate set for product group + location (on product group card)
  4. Multi-tier (New York) tax set for location (on location card)
  5. Tax set for register
  6. Tax set on product card
integer
manualDiscountReasonCodeID# Set this field if row must be marked as manually discounted integer
algorithmVersion Optional calculation algorithm version number. Can be used to return calculated results for a specific version. Valid values are 1,2,3,4 and 5. integer

Response

Field name Type Description
rows array Array elements have the following attributes:

Field nameTypeDescription
rowNumberIntegerRow number
productIDIntegerProduct ID
amountDecimalItem quantity
vatrateIDIntegerVAT / tax rate ID
vatRateDecimalVAT / tax rate as a percentage.
originalPriceDecimalOriginal unit net price. If you specified price manually, this is the manual price. Otherwise this is the price from price lists.
originalPriceWithVATDecimaloriginal price (VAT included)
promotionDiscountDecimalTotal cumulative discount % from promotions.
manualDiscountDecimalAlias: originalDiscount.
Discount % that you set manually.
discountDecimalTotal cumulative discount: promotions + manual.

Note that percentages multiply! Here is an example:
  • Item price: $1
  • Promotional discount: 10%
  • Manual discount: 10%
  • ⇒ Final price:
    $1 - 10% - 10% = $0.81
  • ⇒ Total cumulative discount: 19%.
finalPriceDecimalFinal unit net price, after promotions. (= originalPrice - discount)
finalPriceWithVATDecimalFinal unit price with VAT / tax.
priceBasedTaxThresholdDecimalPrice-based sales tax is used in Massachusetts, US. If item price exceeds a certain threshold, then the part above threshold is taxed at a different, secondary tax rate. This is the threshold value (in dollars).
priceBasedTaxRateDecimalPrice-based sales tax. Secondary tax rate.
rowNetTotalDecimalNet total (unit net price, times quantity).
rowVATDecimalTotal tax or VAT.
rowTotalDecimalRow total with tax or VAT.
nonDiscountableIntegerIs row non-discountable. This is a summarized information about possibility to discount this item, can be used to prevent discounting actions for this item in cart interface. Non discountable cases: gift cards, products marked as non-discountable, products with product group or one of parent product groups marked as non-discountable.
nonTaxablePriceDecimalNon-taxable value

Additional information about discounts that were applied to this invoice line:

This data set can be passed on to saveSalesDocument — Erply backend will use this data for discount reporting.

This data will then also be available through API, with API getAppliedPromotionRecords.

This data set is structured in a slightly unusual way, but this is for making usage easier; you can just gather all the fields prefixed with "promotionRule" and pass them on as input parameters to saveSalesDocument without having to re-convert any arrays.

There can be three types of discounts:

  1. Price list discounts;
  2. Manual discount (applied by the cashier in POS by adjusting price, entering a discount percentage or clicking one of the predefined % buttons);
  3. Promotion discounts.

The following fields describe the first discount on first invoice line:

  • promotionRule1amount1
  • promotionRule1finalPrice1
  • promotionRule1totalDiscount1
  • promotionRule1campaignType1
  • promotionRule1campaignDiscountValue1
  • promotionRule1campaignDiscountPercentage1
  • promotionRule1campaignID1
Then, the second discount on first invoice line:
  • promotionRule1amount2
  • promotionRule1finalPrice2
  • promotionRule1totalDiscount2
  • promotionRule1campaignType2
  • promotionRule1campaignDiscountValue2
  • promotionRule1campaignDiscountPercentage2
  • promotionRule1campaignID2
. If the second invoice line also had promotions, the list of fields continues:
  • promotionRule2amount1
  • ...

Each of these sets of fields therefore describes either a price list discount, a manual discount or a promotion discount, but not multiple at the same time. If the record has a "promotionID", then it is a promotion discount. If the record has a "priceListID", it is a price list discount. If it has neither, it is a manual discount.

To be able to retrieve this information, or to save it back to Erply, you also need the respective additional modules enabled on your Erply account. Please contact customer support for that:

  • Promotion Report module
  • Applied Price Lists module
  • Applied Manual Discount module

promotionRule#campaignID#IntegerApplied promotion ID — if the applied discount was a promotion.
promotionRule#priceListID#IntegerApplied price list ID — if the discount was a price list discount.
promotionRule#name#StringApplied promotion or price list name. (Empty for manual discounts.)
promotionRule#amount#IntegerWhat quantity the promotion / price list applied to, on this particular invoice line. If customer bought 2 or more of this item, but only one was with promotional discount (eg. a Buy One, Get One promotion), then the returned value is 1.
promotionRule#finalPrice#DecimalFinal total of the discounted items (price * quantity) immediately AFTER applying the promotion, price list or manual discount.

Field name is incorrect, but preserved for compatibility.

Note that this is not the same as line total after discount. If the discount only applied to some of the items on the line, this will be the total for these discounted items only.
promotionRule#totalDiscount#DecimalTotal $ discount given to this invoice line.
promotionRule#campaignType#String"ITEMS" or "INVOICE". Type of the promotion, if the applied discount was a promotion.

"ITEMS" for line or item discounts; "INVOICE" for any discounts that applied to the whole document. (Since there is no "invoice discount" concept in Erply, invoice discounts need to be divided proportionally between invoice lines.)
promotionRule#campaignDiscountValue#DecimalDollar discount that was specified in promotion parameters — if this was a dollar discount promotion. (For example, if the promotion was "Get $20 off of all shoes", the field value is 20.)
promotionRule#campaignDiscountPercentage#DecimalPercentage discount as it was defined in promotion description — if this was a percentage discount promotion (eg "10% off").
promotionRule#priceListDiscountType#String"PRICE" or "DISCOUNT".

This field is populated only for applied price lists, not promotions.

"PRICE" if the price list applied a fixed price, "DISCOUNT" if the price list applied a discount percentage.
promotionRule#priceListDiscountPercentage#DecimalPercentage discount as it was defined in price list — if this was a percentage discount (eg "10% off").
promotionRule#manualDiscountPercentage#DecimalManual discount percentage — if this was a manual discount.
netTotal Decimal
vatTotal Decimal
rounding Decimal
total number =netTotal+vatTotal+rounding
automaticCoupons string Eg:"1,4,18"
Superseded by printAutomaticCoupons, see next.
printAutomaticCoupons array List of coupons that should be automatically printed to the customer after the sale has been completed. Array contents:
Field nameTypeDescription
couponIDIntegerCoupon ID
promptCashierIntegerPOS must ask the cashier if they want to print this coupon or not.
printStringPossible values: "AUTOMATICALLY", "PROMPT", "NEVER".
printingCostInRewardPointsDecimalAmount of reward points that this coupon "costs" to to the customer. This amount of points IS AUTOMATICALLY subtracted from customer when you call saveIssuedCoupon to create the coupon. (DO NOT call subtractCustomerRewardPoints yourself.)

FYI: some promotions may cost points, too, and you do need to subtract those manually. See field appliedPromotions below.

The recommended workflow is as follows:
  1. When calling calculateShoppingCart for the last time (you know that shopping cart has been finalized and neither contents nor cart total will not change any more), set getAutomaticCoupons = 1 and take note of the automatic coupon IDs that API returns.
  2. Save the sale with saveSalesDocument.
  3. For each provided coupon ID, call saveIssuedCoupon to generate a unique coupon number for the customer.

    If printing the coupon costs reward points, saveIssuedCoupon will subtract those automatically. If customer does not have a sufficient point balance, saveIssuedCoupon will return error 1042. If you want to be aware of that beforehand, you may issue a getCustomerRewardPoints API call to check customer's point balance.
  4. If you want to provide a paper coupon printout (or an e-mail etc.), you currently need to assemble it yourself. saveIssuedCoupon will return generated ID, coupon name and descriptive text that you can lay out as needed.

Important! API assumes that you are calling calculateShoppingCart to prepare a sales invoice. The list of automatic coupons is provided on the assumption that customer will earn a few more points from the current sale — and those points are already counted in.

Eg.: let's say that customers are always given a coupon when they reach 200 points. If a customer currently has 180 points, you call calculateShoppingCart and basket total value is $25, API will instruct you to print a coupon. (After the sale, the customer would have 205 pts, meaning that they would be eligible for the coupon.)

However, this assumption does not work if you are calling calculateShoppingCart to create an order, quote, lay-by, waybill or prepayment invoice. In that case, ignore the printAutomaticCoupons field and do not issue any coupons.
usedCouponIdentifiers string Eg: "109000002346,109000002351"
Comma-separated list of coupons that had an effect on the given sale (from among all the coupons specified in input parameter couponIdentifiers). If a coupon was effective, it should be redeemed upon sale completion with API call redeemIssuedCoupon. Ineffective coupons should be given back to the customer, to be used at some other occasion.
appliedPromotions array Promotions that were applied to the cart. Each array element contains the following fields:
Field nameTypeDescription
promotionIDIntegerPromotion ID (see getCampaigns)
countIntegerHow many times the promotion was applied.

Certain promotions (all promotions that give a receipt discount, or depend on invoice total) only apply once. BOGO promotions can apply multiple times, depending on the amount that customer buys.
rewardPointsIntegerAmount of reward points that this promotion requires from the customer. This amount of points must be multiplied by the "count" field and subtracted from customer when sale is completed, using subtractCustomerRewardPoints.
information string API analyzes the current shopping cart and customer history, and returns a plain-text message if it makes an interesting observation:
  • Customer should buy one more item (or $x more) and will be eligible for a promotion;
  • Recommended items that customer regularly purchases, and should presumably buy now again;
  • Recommended items that go well with the items already in the shopping cart;
  • When was customer's last visit;
  • What is customer's total purchase history;
  • Etc.
.
This field might not ber enabled by default, so let us know if you want to retrieve this information.
freeExtraProductID integer This field (and the next ones) notify the cashier if there is a promotion "Buy X and get product Y for free", promotion prerequisites have been fulfilled (customer has purchased X), but free gift Y has not been added to the basket yet. This field contains the product ID of the free gift item.
freeExtraProductCode string See previous. Product code of the free gift item.
freeExtraProductName string See previous. Name of the free gift item.
freeExtraNotification string See previous. Free-text notification about the free gift item that is available. Can be displayed in POS UI.