Skip to content

Precision Pricing#


Description#

In industries where products are sold at prices lower than one cent, accurate sub-penny pricing is essential, especially for high-quantity sales. To enable sub-penny pricing, Miva has enhanced various product and order fields to handle up to 8 decimal places of precision.

In version 10.11.00 price and cost fields for Product, Attribute, Option, and Variant support up to 8 digits of precision within the admin interface.

All rounding calculations follow Banker’s Rounding (round to the nearest even number). However, when a product’s price rounds to below $0.01, the minimum extended price will default to $0.01 to prevent items from unintentionally being sold at no charge.

For calculations, prices for items and options will be summed and multiplied by the quantity before rounding occurs at the line-item level. Even for fields where more than two decimal places are not typically used, such as in discounts or fields restricted to two decimal places, calculations will still accurately follow the quantity * price formula to ensure correct pricing.

Basket, Order, and Quote Items#

To support pricing precision and accuracy, the retail, base_price, and price fields for Basket, Order, and Quote items have been updated to support 8-digit precision. Additionally, a new total field has been added to these item structures to represent the final price per item, including options and quantity. This total is rounded to two decimal places using specialized rounding rules, providing consistent pricing at the item level.

Note

Integrators and module developers should use the item-level totals when calculating overall basket, order, or quote totals to ensure accuracy.

Rounding Adjustments#

Rounding operations have been removed from the following discount functions:

  • BasketItemDiscount_Total_Line
  • BasketOptionDiscount_Total_Option
  • BasketOptionDiscount_Total_LineOption_ID
  • OrderItemDiscount_Total_Line
  • OrderOptionDiscount_Total_Option
  • OrderOptionDiscount_Total_LineOption_ID

Round_Item_Total( price ), found in lib/util_public.mv, will round the price to two decimal places, setting the total to $0.01 if the rounded value is zero but the original price is not.

Compatibility and Consistency Adjustments#

Where functions already interact with a related item (e.g., BasketOption_Insert or OrderOption_Insert), new versions of these functions are designed to pass both item and option records to the v10 functions (v10_BasketOption_Insert, v10_BasketOption_Update, etc.). This reduces duplicate record loads and ensures compatibility.

Existing functions are also being renamed for consistency and backward compatibility, such as renaming BasketOption_Insert to BasketOption_Insert_LowLevel. New v10 functions handle recalculation when inserting or updating options.

New Functions#

Summary Loading Functions#

The BasketItemList_Load_Basket_Summary(basket_id, basketitems var) and OrderItemList_Load_Order_Summary(order_id, orderitems var) functions output an array of items containing all raw data fields from the database, with additional fields:

  • unit_quantity: The quantity of the item
  • unit_price: The price of the item, including options, before rounding
  • unit_name: Adjusted to reflect quantity if needed (e.g., "(Qty <quantity>) <original name>")

The unit_price ensures that unit_quantity * unit_price equals total without further rounding. For high-precision prices where exact equality isn’t possible, unit_quantity defaults to 1, and unit_price is set to the total value.

Usage#

<mvt:do file="g.Module_Library_DB" name="l.void" value="BasketItemList_Load_Basket_Summary( g.basket:basket_id, l.settings:basketitems )" />

<mvt:do file="g.Module_Library_DB" name="l.void" value="OrderItemList_Load_Order_Summary( l.settings:order:id, l.settings:basketitems )" />

Example Structure#

This example is from BasketItemList_Load_Basket_Summary() and the structure includes a child product with the type of core

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
[1]:base_price=0.12345678
[1]:basket_id=9
[1]:child_count=1
[1]:children[1]:base_price=5
[1]:children[1]:basket_id=9
[1]:children[1]:code=CORECHARG
[1]:children[1]:group_id=45
[1]:children[1]:line_id=45
[1]:children[1]:name=Core Charge
[1]:children[1]:option_count=0
[1]:children[1]:parent_id=44
[1]:children[1]:price=5
[1]:children[1]:product_id=47
[1]:children[1]:quantity=2
[1]:children[1]:retail=0
[1]:children[1]:subscrp_id=0
[1]:children[1]:subterm_id=0
[1]:children[1]:tax=0
[1]:children[1]:taxable=1
[1]:children[1]:total=10
[1]:children[1]:type=core
[1]:children[1]:unit_name=Core Charge
[1]:children[1]:unit_price=5
[1]:children[1]:unit_quantity=2
[1]:children[1]:upsold=0
[1]:children[1]:variant_id=0
[1]:children[1]:weight=0
[1]:children[1]:wish_id=0
[1]:code=ny__folding-chair-accent-piece-for-small-spaces
[1]:group_id=44
[1]:line_id=44
[1]:name=Folding Chair - Accent Piece for Small Spaces
[1]:option_count=0
[1]:parent_id=0
[1]:price=0.12345678
[1]:product_id=5
[1]:quantity=2
[1]:retail=0.12345678
[1]:sku=NY-003
[1]:subscrp_id=0
[1]:subterm_id=0
[1]:tax=0
[1]:taxable=1
[1]:total=0.25
[1]:type=product
[1]:unit_name=(Qty 2) Folding Chair - Accent Piece for Small Spaces
[1]:unit_price=0.25
[1]:unit_quantity=1
[1]:upsold=0
[1]:variant_id=0
[1]:weight=0.12345678
[1]:wish_id=0
[2]:base_price=5
[2]:basket_id=9
[2]:code=CORECHARG
[2]:group_id=45
[2]:line_id=45
[2]:name=Core Charge
[2]:option_count=0
[2]:parent_id=44
[2]:price=5
[2]:product_id=47
[2]:quantity=2
[2]:retail=0
[2]:subscrp_id=0
[2]:subterm_id=0
[2]:tax=0
[2]:taxable=1
[2]:total=10
[2]:type=core
[2]:unit_name=Core Charge
[2]:unit_price=5
[2]:unit_quantity=2
[2]:upsold=0
[2]:variant_id=0
[2]:weight=0
[2]:wish_id=0

BasketItem_Recalculate_Total(basketitem var)#

This function recalculates the total for a given basket item by summing the base price and option prices, multiplying by quantity, and rounding the result. The total will be stored in the basket item structure but won’t update the database—that’s handled by the calling function.

OrderItem_Recalculate_Total(orderitem var)#

Similar to the basket function, this recalculates the total for order items based on the combined item price, options, and quantity, then rounds the final amount. Like BasketItem_Recalculate_Total, it only updates the structure and not the database.

Validate_Price_Optional( value )#

Validate_Price_Required( value )#

Validate_Price_NonNegative_Optional( value )#

Validate_Price_NonNegative_Required( value )#

These function behave identical to the similarly named Validate_Currency functions except up to 8 decimal digits should be permitted.

The following functions use a Validate_Currency function for validation of price or cost and have been modified to use the new Validate_Price functions instead:

  • Product_Validate_Common
  • JSON_Product_Update

Function Updates#

CurrencyModule_AddFormatting#

The US Currency and Generic Currency modules have been modified in 10.11.00 to no longer explicitly ROUND 2, instead they now use Price_Pad which will ensure the value is output to at least 2 decimal digits while allowing more if necessary.

Example of rounding to 2 decimals with the CurrencyModule_AddFormatting function:

<mvt:do file="g.Module_Store_Module_Currency" name="l.formatted_price" value="CurrencyModule_AddFormatting(g.Store:currency_mod, l.settings:example_value ROUND 2)" />

BasketItem_Insert and BasketItem_Insert_LowLevel#

BasketItem_Insert will calculate a total if not provided, applying the Round_Item_Total function. If BasketItem_Insert_LowLevel finds a null total, it will set it to zero.

OrderItem_Insert and OrderItem_Insert_LowLevel#

OrderItem_Insert will calculate a total if it’s null, while OrderItem_Insert_LowLevel will set the total to zero in such cases.

BasketItem_Update and OrderItem_Update#

Existing update functions have been renamed to *_LowLevel and will only update records without recalculating totals. New update functions (v10_BasketItem_Update and v10_OrderItem_Update) will call the recalculation function if the price or quantity has changed.

Backward compatibility functions BasketItem_Update and OrderItem_Update have been created, which load the original items and pass them to the new update functions.

Basket_SubTotal_Taxable#

Modified to ignore sNN_BasketOptions and return SUM( sNN_BasketItems.total )

BasketItem_Total#

Modified to return SUM( sNN_BasketItems.total ). When combined with the modification to BasketOption_Total (see below), this will retain backwards compatibility.

BasketOption_Total#

Because of the way totals are calculated, it is no longer possible to calculate standalone cumulative option prices across multiple lines. This function has been deprecated, and modified to return a deprecation error. Since Error returns 0, and the only existing callers of this function in the software are functions such as Basket_SubTotal that are using it in combination with BasketItem_Total, the error return of 0 will be seen as an option total of 0.00, and the resulting quantity will be correct even for outdated software.

OrderItem_Total_OrderShipment#

Modified to return SUM( sNN_OrderItems.total ). When combined with the modification to OrderOption_Total_OrderShipment (see below), this will retain backwards compatibility.

OrderOption_Total_OrderShipment#

Because of the way totals are calculated, it is no longer possible to calculate standalone cumulative option prices across multiple lines. This function has been deprecated, and modified to return a deprecation error. Since Error returns 0, and the only existing callers of this function in the software are functions such as OrderItem_Total_OrderShipment that are using it in combination with OrderItem_Total_OrderShipment, the error return of 0 will be seen as an option total of 0.00, and the resulting quantity will be correct even for outdated software.

OrderItem_Total#

Modified to return SUM( sNN_OrderItems.total ). When combined with the modification to OrderOption_Total (see below), this will retain backwards compatibility.

OrderOption_Total#

Because of the way totals are calculated, it is no longer possible to calculate standalone cumulative option prices across multiple lines. This function has been deprecated, and modified to return a deprecation error. Since Error returns 0, and the only existing callers of this function in the software are functions such as Order_Total that are using it in combination with OrderItem_Total, the error return of 0 will be seen as an option total of 0.00, and the resulting quantity will be correct even for outdated software.

BasketOption_Total_Line#

This function, which was added in 10.07.00 has been deprecated. 1. It is currently used for level-II support where the software is calculating unit prices, which are restricted to 2 decimal digits. 2. It is generating a unit-level (quantity of 1) price, which is not guaranteed to be the same as the extended price 3. Callers of this function must be calculating a total price and rounding it at the end, rather than rounding each line and option individually.

Basket_SubTotal#

Removed the call to BasketOption_Total.

Basket_Total#

Removed the call to BasketOption_Total.

OrderShipment_SubTotal#

Removed the call to OrderOption_Total_OrderShipment.

Order_Total#

Removed the call to OrderOption_Total.

JSON Updates#

To support high-precision pricing up to eight decimal places, all JSON output fields related to pricing will now use encodejavascriptnumber. This change affects the following functions:

  • JSON_AttributeTemplateAttribute
  • JSON_AttributeTemplateOption
  • JSON_AttributeTemplateAttributeList_Load_Query
  • JSON_ProductInventoryList_Load_ProductVariants
  • JSON_ProductInventoryList_Load_ProductVariants_Filter
  • JSON_ProductVariantPricing_Load
  • JSON_OrderItem_DetermineVariant
  • JSON_OrderItem_OnDemandColumns
  • JSON_Product_OnDemandColumns
  • JSON_Attribute_Load_Code
  • JSON_AttributeList
  • JSON_Option_Load_Code
  • JSON_OptionList_Load_CodeMatch
  • JSON_OptionList_Load_Attribute
  • JSON_AttributeTemplateOptionList_Load_Attribute
  • JSON_AttributeAndOptionList_Load_Product
  • JSON_ProductAttribute
  • JSON_ProductOption
  • JSON_ProductAttributeAndOptionList_Load_Query
  • JSON_Runtime_Product_AttributeAndOption
  • JSON_Possible_Output
  • JSON_Runtime_Product
  • JSON_ProductInventoryList_Load_ProductKit

JSON Output Updates#

Function JSON_OrderItem_OnDemandColumns has been modified to use sNN_OrderItems.total for its “total” output field instead of calculating it.

encodejavascriptnumber has been implemented across JSON functions handling pricing data to ensure that values retain up to eight decimal places. This will be particularly important for cases where fractional pricing or small units (like grams or milliliters) require precise representation.

Functions updated:

  • JSON_AttributeTemplateAttribute
  • JSON_AttributeTemplateOption
  • JSON_AttributeTemplateAttributeList_Load_Query
  • JSON_ProductInventoryList_Load_ProductVariants
  • JSON_ProductInventoryList_Load_ProductVariants_Filter
  • JSON_ProductVariantPricing_Load
  • JSON_OrderItem_DetermineVariant
  • JSON_OrderItem_OnDemandColumns
  • JSON_Product_OnDemandColumns
  • JSON_Attribute_Load_Code
  • JSON_AttributeList
  • JSON_Option_Load_Code
  • JSON_OptionList_Load_CodeMatch
  • JSON_OptionList_Load_Attribute
  • JSON_AttributeTemplateOptionList_Load_Attribute
  • JSON_AttributeAndOptionList_Load_Product
  • JSON_ProductAttribute
  • JSON_ProductOption
  • JSON_ProductAttributeAndOptionList_Load_Query
  • JSON_Runtime_Product_AttributeAndOption
  • JSON_Possible_Output
  • JSON_Runtime_Product
  • JSON_ProductInventoryList_Load_ProductKit

JSON Input Validation#

JSON_Input_Price#

  • This function will allow entry of prices with up to eight decimal places. This level of precision ensures that products priced in fractions of a unit (e.g., per ounce, per milliliter) are captured accurately.
  • JSON_Input_Price also rejects negative values, aligning with admin and database validations to prevent erroneous or invalid entries. This is especially valuable for e-commerce businesses handling complex pricing structures or promotions.