Skip to content

Resellers — Developer Guide

Phase 1 Sales module. Slug resellers, middleware module:resellers, permissions resellers.*. Hard catalog dependency on Payments. Companion ledger: Reseller Payouts.

Prefer copying Vendors/Companies assignee patterns over inventing new ones.

Backend layout

PiecePath
Modelapp/Models/Reseller.php
EnumsResellerStatusEnum, ResellerActivityTypeEnum
Models (notes/timeline)ResellerNote, ResellerActivity
Serviceapp/Services/Tenant/ResellerService.php (+ ScopesToAssignee)
Controllerapp/Http/Controllers/Tenant/Api/V1/ResellerController.php
Requestsapp/Http/Requests/Tenant/Api/V1/Reseller/*
Resourcesapp/Http/Resources/Tenant/Api/V1/Reseller/*
Policyapp/Policies/ResellerPolicy.php
Eventsapp/Events/Reseller*.php (incl. ResellerNoteAdded)
Subscriberapp/Listeners/ResellerEventSubscriber.php (platform audit)
Link ruleapp/Rules/LinkableReseller.php (invoice reseller_id)
Assignee ruleapp/Rules/EligibleResellerAssignee.php
TestsResellerTest.php, ResellerInviteTest.php, ResellerNotesTimelineTest.php

Domain notes

  • Soft deletes; route binding uses withTrashed() for restore/force.
  • Status enum: active | inactive (default active).
  • Scalar notes on resellers is the profile field. Threaded notes live in reseller_notes and are serialized as note_entries (relationship noteEntries()) to avoid colliding with the scalar.
  • Domain timeline: reseller_activities via ResellerService::recordActivity / timeline. Types include created, updated, assigned, note_added, login_invited, deleted, restored.
  • Rates: commission_rate, owner_commission_ratedecimal(5,2), validated 0–100 on store/update.
  • Assignee scoping via ScopesToAssignee with resellers.assign. Without assign (and not superadmin): list/stats/view limited to assigned_to = actor or user_id = actor. Update without assign is assignee-only (linked login cannot update via that path unless also assignee).
  • resellers.force.delete is not on default admin/manager/staff maps — owner/superadmin only.
  • Invite login (ResellerService::inviteLogin): requires no existing user_id; creates a tenant user via TenantUserService with role ['reseller'] only (exclude_from_lead_auto_assign, no employee, no website leads); unique email per tenant; unique (tenant_id, user_id) on resellers.
  • Protected default role reseller (see config/tenant-protected-roles.php) — ensures invited partners cannot escalate via role assignment APIs that block protected names.
  • Invoice link: optional customer_invoices.reseller_id validated with LinkableReseller (entitlement + assignee scope unless resellers.assign / superadmin).

Permissions

config/tenant-permissions.php:

resellers.view | create | update | delete | restore | force.delete | assign | invite

Default role map (config/tenant-default-role-permissions.php):

RoleGrants
adminview–restore, assign, invite (+ payouts all) — not force.delete
managerview, create, update, assign, invite (+ payouts view/approve)
staffview (+ payouts view)
resellerview (+ payouts view)

Routes use module:resellers then can:resellers.* / policies.

API (tenant)

Base: /api/tenant/v1 — full reference tenant-v1-resellers.md.

MethodPathPermission
GET/resellersview
GET/resellers/statsview
GET/resellers/{reseller}view
POST/resellerscreate
PUT/resellers/{reseller}update
DELETE/resellers/{reseller}delete
POST/resellers/{reseller}/restorerestore
DELETE/resellers/{reseller}/forceforce.delete
POST/resellers/{reseller}/assignassign
POST/resellers/{reseller}/invite-logininvite
GET/resellers/{reseller}/timelineview
POST/resellers/{reseller}/notesupdate

Frontend

SPA mirrors Vendors detail pattern: src/pages/resellers/ with Overview | Notes | Activity | Access tabs (reseller-detail-sheet.tsx). Service: resellerService (addNote, timeline). Nav: permission: resellers.view + module: 'resellers' under Sales.

Tests

bash
php artisan test --compact tests/Feature/Tenant/Reseller
SuiteLocation
Pesttests/Feature/Tenant/Reseller/ (ResellerTest, ResellerInviteTest, ResellerNotesTimelineTest)

Logging

  • Spatie LogsActivity on Reseller (log name resellers)
  • Domain timeline via reseller_activities (ResellerService::recordActivity)
  • PlatformAuditService via ResellerEventSubscriber: reseller_created, reseller_updated, reseller_deleted, reseller_assigned, reseller_login_invited, reseller_note_added

Deferred

  • Cross-workspace identity (Central-linked reseller person across tenants)
  • Reseller portal / branded partner UX
  • Dashboard widgets, import/export

Official documentation for the EloSync SaaS Platform.