Developer notes
File map
Section titled “File map”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 upgradesdesign/backend/templates/addons/kv_vendor_payouts_automation/ views, components, settings info, licence UIdesign/backend/css/addons/kv_vendor_payouts_automation/ styles.less, license.lessvar/langs/en/addons/kv_vendor_payouts_automation.poMoney model
Section titled “Money model”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.
Tables
Section titled “Tables”| 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 |
Safety against double payouts
Section titled “Safety against double payouts”- One payout run at a time: MySQL
GET_LOCKper 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.
Full page cache (4.21)
Section titled “Full page cache (4.21)”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.
License check
Section titled “License check”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).
Extension points
Section titled “Extension points”- 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.