Skip to content

Developer notes

app/addons/kv_vendor_payouts_automation/
addon.xml settings, 4 e-mail templates, 6 tables, 2 privileges
init.php / func.php hooks, settings handlers, licence helpers
controllers/backend/kv_vendor_payouts.php queue, batches, export, balances, schedules,
payout details, overview, statement, cron
controllers/backend/kv_vendor_payouts_automation_license.php "Check now" and licence cron
schemas/menu/*.post.php admin menu (Vendors) and vendor panel menu
schemas/permissions/*.post.php privileges, vendor modes, cron as trusted controller
Tygh/Addons/KvVendorPayoutsAutomation/
Config.php settings and effective schedule (vendor > plan > global)
Schedule.php periods, payout days, period keys (DST-safe)
Balance.php balance, hold, available amount (through core Tygh\VendorPayouts)
Engine.php payout run, pseudo-cron, run lock
Requests.php requests, approve/reject/paid, batches, sync with core
Methods.php payout details (encrypted), IBAN/BIC checks, masking
Exporter.php CSV, PayPal Payouts file, SEPA pain.001.001.03
Statement.php monthly statement
Notifier.php e-mails
Money.php primary currency, minor units, rounding
StorageData.php run marks (files instead of storage_data while the full page cache is on)
License/, Upgrade/ licence client, self-healing upgrades
design/backend/templates/addons/kv_vendor_payouts_automation/ views, components, settings info, licence UI
design/backend/css/addons/kv_vendor_payouts_automation/ styles.less, license.less
var/langs/en/addons/kv_vendor_payouts_automation.po

The add-on keeps no ledger of its own. A request is a row in core ?:vendor_payouts with payout_type = withdrawal:

Request status Core approval_status Balance
Pending approval, Approved P deducted
Paid C deducted
Rejected D not deducted (money back)

Balances are always read through Tygh\VendorPayouts::getBalance(), so the vendor_plans commission hooks and any add-on that hooks vendor_payouts_get_income/_get_balance are applied. The amount on hold is getBalance(['order_id' => held orders]), which nets commission, refunds and direct-payment withdrawals of those orders exactly as core does; it is floored at 0. Comparisons use integer cents; the request amount is rounded down to the decimals of the primary currency. The in-memory core balance cache (Registry keys tagged vendor_payouts_*) is reset after the add-on writes a payout row.

Table Purpose
kv_vpa_requests One row per request: core payout_id (unique), vendor, source (S scheduled, N created by the marketplace, M vendor), period_key (unique per vendor), amount, currency, status (P, A, D paid, R rejected, X removed), method, details-changed flag, batch, reference, reason, balance and hold at creation, timestamps
kv_vpa_batches Batches: count, total, currency, status (O to pay, P paid), export and payment time, reference
kv_vpa_batch_items Batch members with the encrypted snapshot of the payout details
kv_vpa_methods Payout details per vendor (encrypted JSON with the store’s crypt key), masked label, change time
kv_vpa_schedules Overrides per plan (P) or vendor (V); empty/-1 = inherit
kv_vpa_order_marks When an order first reached a hold-start status
  • One payout run at a time: MySQL GET_LOCK per database and table prefix; a second run returns “in progress”.
  • One automatic request per vendor and period: UNIQUE(company_id, period_key); the core row is removed again if the insert loses a race.
  • No new request while the vendor has any pending withdrawal (from the add-on, the vendor or the admin).
  • Status changes are conditional updates (WHERE status IN (...)); the core row is changed only when the update succeeded, and only while the core row is still pending. A core row approved or declined elsewhere is reconciled, never overridden.
  • Files are built from the snapshot taken at approval.
Hook Use
change_order_status_post records the first time an order reaches a hold-start status
vendor_payouts_update_post mirrors approve/decline from core Accounting; adds vendor-requested withdrawals to the queue
delete_company removes payout details, schedule override and hold marks (requests and batches stay for the audit trail)
delete_order removes the hold mark
before_dispatch pseudo-cron on admin/vendor panel GET pages (at most every 15 minutes)

All hook signatures are identical in 4.17.2 and 4.21.2.

The add-on has no storefront output and writes nothing during storefront requests. Run marks go through StorageData (files while the full page cache is active), so no storage_data write purges the page cache.

fn_kv_vendor_payouts_automation_is_licensed() (License\Client::isActive()) gates the payout run (cron, pseudo-cron), Create requests now, batch file downloads, statements and the vendor Payouts page. The queue actions (approve, reject, mark paid) are never gated, so money already promised can always be settled. The licence check itself runs only in the admin panel or from cron (php admin.php --dispatch=kv_vendor_payouts_automation_license.check).

  • Core hooks of Tygh\VendorPayouts (vendor_payouts_get_balance, vendor_payouts_update_post, …) see every request the add-on creates.
  • Any add-on that changes vendor income through core hooks changes the available amount automatically.
  • Statements compare the sum of their lines with the core period balance; a difference caused by another add-on is shown as Other adjustments.