Developer notes
Overview · Installation · Configuration · Usage · FAQ
Technical reference for developers who customize Wholesale Price Lists.
Price resolution
Section titled “Price resolution”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).
- 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. - Applicable lists: status A, inside the validity dates, matching storefront (Ultimate:
company_id0 or the current storefront), assigned to the user or to one ofauth.usergroup_ids(groups above 1). Cached per request. - Per list: the product’s rows (or the variation parent’s rows, when
product_variationsis active and the variation has none); the row with the highestlower_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. - Several lists: lowest price, or the first list in priority order that has a price (setting
resolution). - With
never_above_regularthe result is capped at the core price for the same quantity. Rounded to 2 decimals, in the primary currency (core converts for display).
Hooks used
Section titled “Hooks used”| 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.
Template data
Section titled “Template data”$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-*).
Data model
Section titled “Data model”| 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) |
Admin endpoints
Section titled “Admin endpoints”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).
License check
Section titled “License check”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.
Not in 1.0.0
Section titled “Not in 1.0.0”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.
File map
Section titled “File map”| 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 |