Skip to content

Developer notes

Table Contents
cscart_kv_ppc_products One row per currency-priced product: product_id, company_id, currency_code, price and list_price in the product currency (4 decimals), rounding ('' store rule, N none), rate and converted_at of the last conversion.
cscart_kv_ppc_tiers Quantity and customer group prices in the product currency: product_id, usergroup_id, lower_limit, type (A absolute, P percent), value. The base price (quantity 1, all customers) is the product row itself.
cscart_kv_ppc_rates Per currency: the rate last applied to all its products, and the cursor of an interrupted run. A difference to cscart_currencies.coefficient means “rate changed”.
cscart_kv_ppc_log, cscart_kv_ppc_log_items Runs (trigger, currency, rates, counts, status) and the changed products with old and new primary prices.

The add-on writes nothing to cscart_storage_data and has no storefront output.

primary = round(source × coefficient) with the store rounding rule (Rounding::apply()), for the base price, the list price and absolute tiers; percent tiers keep their percentage (core stores them with the base price). Prices are written through core fn_update_product_prices() (so the update_product_prices hook of other add-ons fires), the list price and updated_timestamp with one UPDATE. On CS-Cart (Ultimate), the rows of cscart_ult_product_prices of every storefront the product is shared with are rewritten too. Only products whose stored prices differ are written.

A run (Recalculator::run()) walks the products of a currency in batches of 200 with a time budget; a run that runs out of time stores a cursor and continues later (cron, Recalculate now). When a run touches more than 100 products and the price history add-on is installed, KV_OPH_NO_SHUTDOWN is defined so that add-on records the changes in its own cron instead of at the end of the request.

Hook Use
update_product_pre kv_price_currency in the product data: validated, saved later, converted values put into price, list_price and prices so core saves them. Without it, a change of price, list_price or prices of a currency-priced product is a direct edit (converted back, or replaced by the conversion).
update_product_post Saves the currency prices, logs the change, applies the currency to variations.
update_currency_post Rate changes: recalculates the currencies whose coefficient differs from the rate last applied.
global_update_products_post Products → Global update.
variation_group_save_group New variations inherit the parent’s currency.
clone_product, delete_product_post Copy / remove the currency prices.
get_products Filter kv_price_currency (EUR, any, primary) for the admin list and the REST API.
get_products_post, get_product_data_post REST API only (API defined): adds kv_price_currency.

Every handler runs inside fn_kv_product_price_currency_safe(): an error is logged and never breaks the product save.

kv_price_currency: {
currency_code: "EUR" | "" (remove),
price, list_price, // in the product currency
rounding: "default" | "none",
tiers: [{lower_limit, usergroup_id, type: "A" | "P", value}],
apply_variations: "Y" // optional, product form
}

Read-only in responses: rate, converted_at.

schemas/exim/products.post.php adds the columns Price currency, Currency price, Currency list price (process_put buffers, post_processing fn_kv_product_price_currency_exim_apply() applies after the rows are saved). schemas/advanced_import/products.post.php adds example values.

php admin.php --dispatch=kv_product_price_currency.cron or index.php?dispatch=kv_product_price_currency.cron&cron_key=<key>: Recalculator::cron() (pending currencies and rate drift, log pruning). Returns JSON.

app/addons/kv_product_price_currency/
addon.xml init.php func.php
controllers/backend/kv_product_price_currency.php Price currencies page, Recalculate now, bulk, log, console cron
controllers/backend/products.post.php data of the product block, list column, search filter
controllers/frontend/kv_product_price_currency.php cron URL
schemas/menu/menu.post.php schemas/permissions/admin.post.php
schemas/exim/products.post.php schemas/advanced_import/products.post.php
schemas/settings/variants.functions.post.php currencies for vendors
Tygh/Addons/KvProductPriceCurrency/
Config.php Rounding.php Converter.php Store.php Writer.php Recalculator.php
Bulk.php Hooks.php Exim.php Log.php License/ Upgrade/
design/backend/templates/addons/kv_product_price_currency/
hooks/products/product_update_price.post.tpl price currency block
hooks/products/list_price.post.tpl list column
hooks/products/search_form.post.tpl search filter
hooks/products/update_qty_discounts.pre.tpl note on the Quantity discounts tab
views/kv_product_price_currency/ manage.tpl, log.tpl, components/
settings/cron_info.tpl license/
design/backend/css/addons/kv_product_price_currency/styles.less
js/addons/kv_product_price_currency/func.js live preview, tiers, read-only core price fields
var/langs/en/addons/kv_product_price_currency.po

The add-on ships the generated License\Client. Gated (nothing happens without a valid license): conversions on product save, direct-edit handling, recalculation on rate changes, cron and Recalculate now, bulk assignment, the import columns and REST writes. Not gated: stored data, product prices, admin pages (read-only), cloning and deleting products.