Skip to content

Developer notes

Overview · Installation · Configuration · Usage · FAQ

Technical reference for developers who customize Wholesale Price Lists.

All prices come from one method, Tygh\Addons\KvWholesalePriceLists\Pricing::resolve($product_id, $qty, $core_price, $auth). It returns ['price' => float, 'list_id' => int] or null (no list price).

  1. Applies only to signed-in customers (auth.user_id > 0), on the storefront, in order management (ORDER_MANAGEMENT) and in the API, the same areas where core uses user group prices, and only while the add-on is enabled and licensed.
  2. Applicable lists: status A, inside the validity dates, matching storefront (Ultimate: company_id 0 or the current storefront), assigned to the user or to one of auth.usergroup_ids (groups above 1). Cached per request.
  3. Per list: the product’s rows (or the variation parent’s rows, when product_variations is active and the variation has none); the row with the highest lower_limit <= qty. Fixed → value; percent → core_price × (1 − value/100). Without a matching row the list’s default discount applies to products that have no rows in the list.
  4. Several lists: lowest price, or the first list in priority order that has a price (setting resolution).
  5. With never_above_regular the result is capped at the core price for the same quantity. Rounded to 2 decimals, in the primary currency (core converts for display).
Hook Purpose
get_product_price_post Cart, checkout, order management, API and other code that calls fn_get_product_price() (for example quote add-ons) get the list price for the total quantity.
gather_additional_products_data_params Loads the rows of a whole product batch in one query (no N+1 in product lists).
gather_additional_product_data_before_options Storefront catalog display: price and base price, quantity table, $product.kv_wpl (regular price, saving). Skipped for cart products and while the EU Omnibus add-on records guest prices.
seo_dispatch_before_display_before_cache The seo add-on caches the schema.org product markup per product for all visitors; the key gets the customer’s list set.

Block cache: schemas/block_manager/block_cache_properties.post.php adds the four tables as update handlers and a callable handler fn_kv_wholesale_price_lists_block_cache_key() (the customer’s list set, empty for guests) to every cached block, so product blocks are never shared between customers with different lists.

Full page cache (4.21): list prices exist only for signed-in customers, whose pages core never caches; guest pages contain no list data. The account page is not a cacheable dispatch. Nothing is written on storefront GET requests.

$product.kv_wpl on catalog products: regular (core price, quantity 1), price (list price), saving_pct, applied (true when the list price is below the regular price), list_id. Template hooks: products:old_price (post, regular price line), products:prices_block (post, “Your price”), profiles:my_account_menu (post, account link). Styles in css/addons/kv_wholesale_price_lists/styles.less (classes kv-wpl-*).

Table Columns
?:kv_wpl_lists list_id, company_id (Ultimate storefront, 0 = all), name, description, customer_note, status (A/D), priority, valid_from, valid_to (unix, 0 = open; valid_to is the end of the day), default_discount, created_at, updated_at
?:kv_wpl_prices list_id, product_id, lower_limit, price_type (F fixed / P percent), value; primary key (list_id, product_id, lower_limit), key (product_id, list_id)
?:kv_wpl_list_users list_id, user_id
?:kv_wpl_list_usergroups list_id, usergroup_id (2 = all registered customers)

kv_wholesale_price_lists.manage | add | update (GET), update, update_prices, import, delete, m_delete, m_status.A|D (POST), export and template (CSV downloads). Privileges view_kv_wholesale_price_lists / manage_kv_wholesale_price_lists (section Users). Vendors: no access.

Storefront: kv_wholesale_price_lists.my_prices and kv_wholesale_price_lists.csv (signed-in customers only).

Tygh\Addons\KvWholesalePriceLists\License\Client (generated) verifies the license. Gate: Config::isActive() in Pricing::appliesTo() (all price hooks), the storefront display flags and the account page. Not gated: the admin pages, import and export, so your lists can always be maintained.

Vendor-defined lists on Multi-Vendor, category-level rules, a REST API entity, list prices in sorting, price filters and feeds, recording the applied list on order lines.

Path Purpose
app/addons/kv_wholesale_price_lists/addon.xml Settings, tables, privileges
init.php, func.php Hook handlers, helpers, fn_kv_wholesale_price_lists_is_licensed()
Tygh/Addons/KvWholesalePriceLists/Pricing.php Price resolution, prefetch, catalog display, quantity breaks
Lists.php, Csv.php, Config.php List storage and editor, CSV import/export, settings
License/, controllers/backend/kv_wholesale_price_lists_license.php License check (generated)
Upgrade/Synchronizer.php Self-healing upgrades (generated)
controllers/backend/kv_wholesale_price_lists.php Admin pages
controllers/frontend/kv_wholesale_price_lists.php, init.post.php My price list, CSV, storefront flags
schemas/ Menu, permissions (admin, vendor), block cache properties
design/backend/templates/addons/kv_wholesale_price_lists/ Admin templates
var/themes_repository/responsive/templates/addons/kv_wholesale_price_lists/ Account page and hook templates
js/addons/kv_wholesale_price_lists/func.js Quantity break rows in the admin editor