Vendors — Developer Guide
Mirror of the Companies developer guide. Prefer copying those patterns over inventing new ones.
Backend layout
| Piece | Path |
|---|---|
| Models | app/Models/Vendor.php, VendorNote, VendorActivity |
| Enums | app/Enums/Tenant/VendorStatusEnum, VendorActivityTypeEnum |
| Service | app/Services/Tenant/VendorService.php (+ ScopesToAssignee) |
| Controller | app/Http/Controllers/Tenant/Api/V1/VendorController.php |
| Requests | app/Http/Requests/Tenant/Api/V1/Vendor/* |
| Resources | app/Http/Resources/Tenant/Api/V1/Vendor/* |
| Policy | app/Policies/VendorPolicy.php |
| Events | app/Events/Vendor*.php |
| Subscriber | app/Listeners/VendorEventSubscriber.php (audit + assignment notification) |
| Notifications | app/Notifications/Tenant/Vendor/VendorAssignedNotification.php |
| Tests | tests/Feature/Tenant/Vendor/VendorTest.php |
Domain notes
- Assignee scoping via
ScopesToAssigneewithvendors.assign; without it, users only see vendors assigned to them (view/update/list/stats). vendors.force.deleteis not granted to any default role — owner/superadmin only, matching Companies/Leads/Tasks/Contacts.- No
contactsrelationship — Vendor is a first-class, standalone entity (unlike Companies). - No
industry,source, orsource_metafields. statusenum (active|inactive, defaultactive) replaces the workflow fields used by other modules.- Assignee eligibility mirrors Companies (
EligibleVendorAssignee/User::isEligibleLeadAssignee). - Soft delete only — no stage/status workflow beyond the
active/inactiveflag. - Purchasing hub (v1):
VendorPartyPurchasingPanel— PO/expense spend activity (not AP payable). Backend:VendorPartyPurchasingSummaryService,VendorAccountStatementService. Draft + cancelled POs/expenses excluded. Statement includesopening_balance(pre-period activity) andbalance_due(cumulative activity as ofto). Vendor PDF deferred. List deep links?vendor=on purchase orders and expenses.
Permissions
config/tenant-permissions.php:
vendors.view | create | update | delete | restore | force.delete | assignRoutes use module:vendors then can:vendors.* / policies.
API (tenant)
Base: /api/tenant/v1 — full reference tenant-v1-vendors.md.
| Method | Path | Permission |
|---|---|---|
| GET | /vendors | view |
| GET | /vendors/stats | view |
| GET | /vendors/{vendor} | view |
| GET | /vendors/{vendor}/timeline | view |
| POST | /vendors | create |
| PUT | /vendors/{vendor} | update |
| DELETE | /vendors/{vendor} | delete |
| POST | /vendors/{vendor}/restore | restore |
| DELETE | /vendors/{vendor}/force | force.delete |
| POST | /vendors/{vendor}/assign | assign |
| POST | /vendors/{vendor}/notes | update |
| GET | /vendors/{vendor}/purchasing-summary | view |
| GET | /vendors/{vendor}/statement | view |
Auth login/me include modules: string[] for SPA gating.
Frontend
| Piece | Path |
|---|---|
| Page | src/pages/vendors/vendors-page.tsx (table + filters + KPIs) |
| Form | vendor-form.tsx + vendor-form-page.tsx |
| Detail | vendor-view-page.tsx (Details, Notes, Timeline; purchasing hub) |
| Statement | src/pages/crm/party-statement-page.tsx (VendorStatementPage) |
| Service | vendorService in src/api/services.ts (purchasingSummary, statement) |
| Nav | permission: vendors.view, module: 'vendors' — new Purchasing group after Billing |
| Party purchasing | Hub + deep links + in-app statement; Pest PartyBilling suite |
Tests
bash
# Backend
php artisan test --compact tests/Feature/Tenant/Vendor/VendorTest.php
php artisan test --compact tests/Feature/Tenant/PartyBilling/PartyBillingSummaryAndStatementTest.php
# Frontend
npm run typecheck && npm run lint && npm run build
npm run test:e2e:vendors| Suite | Location |
|---|---|
| Pest | tests/Feature/Tenant/Vendor/VendorTest.php; PartyBilling suite |
| E2E | e2e/tests/vendors/, npm run test:e2e:vendors |
Logging
- Spatie
LogsActivityonVendor(log namevendors) - Domain
vendor_activitiestimeline PlatformAuditServiceviaVendorEventSubscriber
Intentional differences from Companies
| Companies | Vendors |
|---|---|
industry, source, source_meta fields | tax_id, payment_terms, currency, status fields |
Linked contacts relationship | No contacts relationship |
| No status enum | status enum (active/inactive) |
| Category: CRM | Category: Purchasing (new) |
Deferred
- Purchase Orders (Phase 4 Milestone 2, depends on Vendors)
- Expenses (Phase 4 Milestone 3, soft dependency on Vendors)
- Vendor scorecards / performance tracking
- Dashboard widgets for Vendors
- Communication template placeholders for Vendors