Skip to main content

Vendor Reports & Appeals

Version Pro Updated

React SPA (since v4.40.0)

The Vendor Reports admin page was migrated to a React SPA in v4.40.0 (Faz 6). Data is fetched via /wp-json/mhm-rentiva/v1/vendor-reports/* REST endpoints. Components: VendorReportsPage, FilterBar, ReportTable, DetailView, ActionForm, StatusBadge. Flash messages use the window.mhmRentivaVendorReports.flash pattern (PHP enqueue_assets() reads $_GET['updated']/$_GET['error'] before history.replaceState strips params).

The Vendor Report system gives vendors a structured channel to escalate issues to the platform administrator and to appeal automated actions (like withdrawal penalties). One custom table, one shared modal, one admin page — five different contexts trigger it. Requires a Pro license.

Introduced in v4.35.0.


What is a vendor report?

A vendor report is a structured message from a vendor to the platform administrator. It is not a customer-facing message and not a public review. The system supports five distinct contexts:

ContextUsed forWhere the trigger lives
bookingCustomer issues (no-show, damage, dispute)"Sorun Bildir" button on each vendor booking card
vehicleAppeals against a paused/withdrawn vehicle"İtiraz Et" button on listings page (paused/withdrawn states only)
vehicle_actionReason capture during withdraw/pause — suspends the penaltyModal opens automatically when the vendor clicks Withdraw or Pause
penaltyAppeal an already-applied penalty ledger entry"Appeal" button on each row of the score history table
general"Yöneticiye Yaz" — direct line to adminFooter link on every vendor panel page

All five share the same data structure (one row in wp_mhm_rentiva_vendor_reports) and the same admin UI. Side-effects on resolution differ per context.


Vendor flow — submitting a report

From the booking card

  1. Vendor opens Hesabım → Rezervasyonlar
  2. Each booking card has a "Sorun Bildir" button next to "View Details"
  3. Click → modal opens with title and description fields
  4. Submit → the report is saved with context_type=booking, context_id=<booking_id>, status=open
  5. Email goes to the platform administrator

Withdrawing a vehicle (the Not 2 augment)

The withdrawal flow is the most consequential context. By default, withdrawing a vehicle deducts 10 points from the vendor's reliability score and writes a debit entry to the financial ledger (the size depending on the vendor's withdrawal history — first one is free, then 10% / 25% of monthly average revenue).

With v4.35.0 the vendor can capture a reason while withdrawing:

  1. Vendor opens Hesabım → İlanlar
  2. Click "Çek" on an active vehicle
  3. The reason capture modal opens (replacing the previous browser confirm() dialog)
  4. Vendor enters a reason — minimum 20 characters required
  5. Submit → two things happen in sequence:
    • A vehicle_action report is created with status=open
    • The withdrawal AJAX is called
  6. Inside VehicleLifecycleManager::withdraw(), the new mhm_rentiva_before_apply_penalty filter runs. The PenaltySuspensionHook callback sees the open report and returns false. Score deduction and ledger debit are skipped.
  7. The vehicle still transitions to withdrawn (post status, lifecycle meta, cooldown date all set), but the financial penalty is suspended pending admin review.

Same flow applies to pause actions.

Appealing a past penalty

  1. Vendor opens Hesabım → Güvenilirlik & Cezalar → Score History
  2. Each row that represents a penalty (negative delta tied to a vehicle) has an "Appeal" button
  3. Click → modal opens, title pre-filled with Appeal: <event> on <vehicle>
  4. Submit → report saved with context_type=penalty, context_id=<ledger_uuid>

Note: penalty appeals do not currently reverse the applied ledger entry — that requires the compensating-entry helper landing in v4.36.0+. Resolution updates the report status, sends the vendor an email, and marks the appeal as upheld for record-keeping.

Contacting the administrator

Every vendor panel page has a "Yöneticiye Yaz" link in the footer. This opens the modal with context_type=general (no context_id) and lets the vendor send a free-form message to the admin. Use this for feature suggestions, account questions, or anything that doesn't fit one of the other four contexts.


Admin flow — resolving a report

List page

Yönetim → MHM Rentiva → Bayi Raporları opens a paginated list filtered by status (default: open) and context. Columns:

  • Report ID
  • Vendor name + ID
  • Context label (Rezervasyon / Araç / Araç işlemi / Ceza itirazı / Genel)
  • Title (link to detail)
  • Status pill
  • Created date
  • Action button (Open detail)

Detail page

Clicking a report opens the detail view with:

  • Report title (heading)
  • Status, vendor, context (with context_id link if applicable)
  • Submitted timestamp
  • Full description in a styled block
  • Existing admin note (if any)
  • Action form (only for non-terminal reports):
    • Optional admin note textarea
    • Three submit buttons: Mark as Resolved, Reject, Mark In Review

Resolution side-effects

Context"Mark as Resolved""Reject"
bookingStatus update + email to vendorSame
vehicleSameSame
vehicle_actionNo-op — penalty was already suspended at withdrawal time, vendor keeps their scoreapply_deferred_penalty() runsReliabilityScoreCalculator::update() recomputes (the withdrawal is in state, so score drops) and PenaltyRecorder::record_penalty() writes the deferred ledger debit
penalty(v4.36.0+ — ledger compensating entry helper)No-op (penalty already applied; rejection just closes the appeal)
generalStatus update + emailSame

Both resolutions send the vendor an email (vendor_report_resolved template) with the admin note and the new status.

Mark In Review

Sets status=in_review. Penalty suspension stays active (in_review counts as "open" for the filter callback). No vendor email — this is an internal flag for triage.


The penalty filter — mhm_rentiva_before_apply_penalty

Two filter hook points wrap the score deduction and the ledger entry. Plugins or themes can hook this filter to introduce additional suspension reasons beyond vendor reports:

/**
* @param bool $apply Whether to apply the penalty. Default true.
* @param int $vehicle_id Vehicle post ID.
* @param int $vendor_id Vendor user ID.
* @param string $reason Penalty reason ('withdrawal').
* @param float $penalty Pre-calculated penalty amount.
*/
add_filter('mhm_rentiva_before_apply_penalty', function ($apply, $vehicle_id, $vendor_id, $reason, $penalty) {
if ($reason === 'withdrawal' && my_holiday_freeze_active()) {
return false; // suspend penalties during a platform-wide freeze
}
return $apply;
}, 20, 5);

The plugin's own PenaltySuspensionHook registers at priority 10. Any filter callback that returns false blocks the penalty for that hook fire; an open vendor_action report is just one of multiple possible reasons.


Database

New custom table {prefix}mhm_rentiva_vendor_reports:

ColumnTypeNotes
idBIGINT UNSIGNED AUTO_INCREMENTPrimary key
vendor_idBIGINT UNSIGNEDIndexed
context_typeVARCHAR(20)booking / vehicle / vehicle_action / penalty / general
context_idVARCHAR(64)Integer ID, ledger UUID, or NULL
titleVARCHAR(255)
descriptionLONGTEXTMin 20 chars enforced at service layer
statusVARCHAR(20)open / in_review / resolved / rejected
admin_noteLONGTEXTNULL until first admin action
admin_user_idBIGINT UNSIGNEDNULL until first admin action
created_atDATETIME
updated_atDATETIME
resolved_atDATETIMESet when status transitions to terminal (resolved/rejected)

Indexes: vendor_id, context_type, context_id, status, composite (vendor_id, status), composite (context_type, context_id, status) (the open-report lookup hot path), created_at.

Migration class: src/Core/Database/Migrations/VendorReportsMigration.php. Registered in DatabaseMigrator::run_migrations(). DatabaseMigrator::CURRENT_VERSION bumped 3.5.0 → 3.6.0 to trigger the migration on existing installs.


Lite vs Pro

The entire vendor report system is gated by Mode::canUseVendorMarketplace(). On Lite plans:

  • The admin "Bayi Raporları" submenu is not registered.
  • The AJAX handler returns 403.
  • The shared modal is not enqueued.
  • The penalty filter callback is still registered but always returns apply unchanged (no open reports because vendors can't file any).

In effect, Lite vendors withdraw vehicles the old way — direct penalty, no reason capture, no appeal path. Pro upgrade unlocks the full system.

For local testing without a real Pro token, add define('MHM_RENTIVA_DEV_PRO', true); to wp-config.php (works only when WP_DEBUG=true).


Developer extension points

Hook / ClassPurpose
mhm_rentiva_before_apply_penaltyFilter — gate the score deduction and ledger entry. 5-arg signature.
mhm_rentiva_vendor_report_createdAction — fires after a report is persisted. Email subsystem listens. 3 args.
mhm_rentiva_vendor_report_resolvedAction — fires after status changes to terminal. 3 args (report_id, vendor_id, new_status).
VendorReportRepositoryPublic API: create(), find(), update_status(), find_by_vendor(), has_open_report_for(), reset_has_open_cache()
VendorReportServicePublic API: create_report(), resolve_report(), reject_report()

React Components (v4.40.0+)

ComponentPurpose
VendorReportsPageRoot — list view + detail view orchestration
FilterBarStatus and context filter controls
ReportTablePaginated, filterable report list
DetailViewFull report detail with vendor info and description
ActionFormResolve / Reject / Mark In Review form with admin note
StatusBadgeColor-coded status pill (open / in_review / resolved / rejected)

REST Endpoints:

  • GET /wp-json/mhm-rentiva/v1/vendor-reports — paginated list, filterable by status and context
  • GET /wp-json/mhm-rentiva/v1/vendor-reports/{id} — single report detail

All endpoints require manage_options capability.


Changelog

DateVersionNote
06.05.20264.40.0Full React SPA migration. VendorReportsPage, FilterBar, ReportTable, DetailView, ActionForm, StatusBadge. Flash flag pattern via window.mhmRentivaVendorReports.flash.
23.04.20264.27.2Documentation synchronized with the current plugin release.
05.04.20264.35.0Initial version — vendor report system introduced. 5 contexts (booking, vehicle, vehicle_action, penalty, general), penalty suspension filter, custom DB table.

See also