Skip to content

Developer notes

app/addons/kv_sales_reports_pro/
addon.xml settings, tables, privileges, e-mail template
init.php, func.php hook registration and handlers, settings variants
controllers/backend/kv_sales_reports_pro.php reports, export, presets, digests, sync, rebuild, cron
controllers/backend/kv_sales_reports_pro_license.php license "Check now"
controllers/frontend/kv_sales_reports_pro.php view counter endpoint (POST hit)
schemas/menu/menu.post.php, menu_vendor.post.php
schemas/permissions/admin.post.php, vendor_multivendor.post.php, trusted_controllers.post.php
schemas/full_page_cache/varnish.post.php own tables ignored as cache tags
Tygh/Addons/KvSalesReportsPro/
Config.php settings, counted/refund/return statuses, RFM thresholds, licence state
Sync.php report snapshot (full build, dirty queue, updated_at safety net, reconcile)
Customers.php customer identity (user account or e-mail)
Counter.php product views and cart adds per day
Filter.php periods, comparison, buckets, SQL scope
Report.php report queries
View.php view data and formatting
Chart.php inline SVG chart
Digest.php, Presets.php, Csv.php, Money.php, Meta.php
License/, Upgrade/ licence client, self-healing upgrades
design/backend/templates/addons/kv_sales_reports_pro/ views, components, hooks
design/backend/css/addons/kv_sales_reports_pro/styles.less
js/addons/kv_sales_reports_pro/admin.js (admin) and func.js (storefront view counter)
var/themes_repository/responsive/templates/addons/kv_sales_reports_pro/hooks/index/ view counter element + script

The reports never scan the core order tables. Sync writes one row per order (?:kv_srp_orders) and per order line (?:kv_srp_items):

  • Full build (install, Rebuild): every order by order_id, in batches of 500.
  • Dirty queue (?:kv_srp_dirty): place_order, change_order_status_post and the RMA hook rma_update_details_post queue the order; it is processed on the next run.
  • Safety net: orders whose updated_at moved past a watermark (kept 5 minutes behind the clock) are re-read, so direct database changes are picked up too.
  • Deletes: delete_order removes the rows; a daily reconcile removes rows of orders deleted outside the hooks.
  • Runs: the cron dispatch, report page views (at most 1.5 s), “Update now” (AJAX steps).

Parent orders of Multi-Vendor checkouts and incomplete orders (N) are never stored; all other statuses are, and the counted statuses are applied when a report is read, so changing them needs no rebuild.

Per order: totals from ?:orders, taxes from order_data type T, items an RMA return took out of the order from type H (they are added back to gross sales and units), accepted returned items from ?:rma_returns / ?:rma_return_products. Item revenue = price x quantity x (1 - cart discount / line total), rounded so the lines add up exactly.

Measured on a test store with 200,000 orders (242,604 order rows including Multi-Vendor parents, 424,000 lines, 56,000 customers): full build 35 s with 50 MB peak memory; 500 changed orders 0.6 s; every report over 3 years under 2 s.

Table Contents
kv_srp_orders One row per order: checkout, company, storefront, customer, status, day, month, totals, gross, returned, refunded, units
kv_srp_items One row per order line: product, checkout, company, storefront, status, day, units, revenue, returns
kv_srp_customers Customer identity: u:<user_id> for accounts, e:<e-mail> for guests
kv_srp_views Views and cart adds per day, product and storefront
kv_srp_presets, kv_srp_digests Saved reports and e-mail digests
kv_srp_dirty, kv_srp_meta Sync queue; watermarks and run marks (no ?:storage_data writes)

All tables are InnoDB and removed on uninstall.

Identical in 4.17.2 and 4.21.2: place_order, change_order_status_post, delete_order, post_add_to_cart (app/functions/fn.cart.php), dispatch_before_display (fn.control.php), rma_update_details_post (core RMA add-on). Storefront template hooks: index:scripts (post), index:footer (post).

Product pages get a hidden element with the product id and the endpoint URL; it is the same for every visitor. The storefront script sends one navigator.sendBeacon POST per product and browser session (remembered in sessionStorage). The endpoint writes one counter row and nothing else; the table is listed in ignore_cache_tags, so no page is purged. Cart adds are counted in post_add_to_cart (a POST request).

php admin.php --dispatch=kv_sales_reports_pro.cron

Runs the sync (up to 10 minutes from the console, 50 s over HTTP) and sends due digests. Over HTTP it needs cron_password.

The licence client (License\Client) verifies the key for the store’s domain once a day, only in the admin panel or from cron. Without a valid license: no snapshot sync, no view counter on product pages, no digests; reports and exports keep working with the data collected so far.