Skip to content

Companies — Developer Guide

Mirror of the Contacts developer guide / Leads developer guide. Prefer copying those patterns over inventing new ones.

Backend layout

PiecePath
Modelsapp/Models/Company.php, CompanyNote, CompanyActivity
Enumapp/Enums/Tenant/CompanyActivityTypeEnum
Serviceapp/Services/Tenant/CompanyService.php (+ ScopesToAssignee)
Controllerapp/Http/Controllers/Tenant/Api/V1/CompanyController.php
Requestsapp/Http/Requests/Tenant/Api/V1/Company/*
Resourcesapp/Http/Resources/Tenant/Api/V1/Company/*
Policyapp/Policies/CompanyPolicy.php
Eventsapp/Events/Company*.php
Subscriberapp/Listeners/CompanyEventSubscriber.php (audit + assignment notification)
Notificationsapp/Notifications/Tenant/Company/CompanyAssignedNotification.php
Placeholdersapp/Services/Tenant/CommunicationTemplates/Providers/CompanyPlaceholderProvider.php
Teststests/Feature/Tenant/Company/CompanyTest.php

Domain notes

  • Assignee scoping via ScopesToAssignee with companies.assign; without it, users only see companies assigned to them (view/update/list/stats).
  • companies.force.delete is not granted to any default role — owner/superadmin only, matching Leads/Tasks/Contacts.
  • Contact → Company linkage: contacts.company_id (nullable FK). ContactService resolves writes so that when company_id is set, the legacy company string is synced to the linked Company name. List/detail resources expose linked_company (id, uuid, name) when loaded.
  • Assignee eligibility mirrors Leads/Contacts (EligibleCompanyAssignee / User::isEligibleLeadAssignee).
  • Soft delete only — no stage/status workflow.
  • Party billing hub: same pattern as Contacts (CustomerPartyBillingPanel with partyKind: 'company'). Backend reuses customer statement services scoped by company_id. List deep links ?company= on invoices, payments, quotations, credit notes. Statement includes opening_balance + balance_due; PDF available.

Permissions

config/tenant-permissions.php:

companies.view | create | update | delete | restore | force.delete | assign

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

API (tenant)

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

MethodPathPermission
GET/companiesview
GET/companies/statsview
GET/companies/{company}view
GET/companies/{company}/timelineview
POST/companiescreate
PUT/companies/{company}update
DELETE/companies/{company}delete
POST/companies/{company}/restorerestore
DELETE/companies/{company}/forceforce.delete
POST/companies/{company}/assignassign
POST/companies/{company}/notesupdate
GET/companies/{company}/billing-summaryview
GET/companies/{company}/statementview
GET/companies/{company}/statement.pdfview

Auth login/me include modules: string[] for SPA gating.

Frontend

PiecePath
Pagesrc/pages/companies/companies-page.tsx (table + filters + KPIs)
Formcompany-form-dialog.tsx
Detailcompany-view-page.tsx (details, notes, activity; billing hub)
Statementsrc/pages/crm/party-statement-page.tsx (CompanyStatementPage)
ServicecompanyService in src/api/services.ts (billingSummary, statement, downloadStatementPdf)
Navpermission: companies.view, module: 'companies' (between Leads and Contacts)
DashboardRecentCompaniesWidget (recent_companies widget) + create_company quick action in tenant-dashboard-widgets.tsx / tenant-dashboard-page.tsx
Contact linkcontact-form-dialog.tsx company picker when module:companies + companies.view; list/detail show linked_company?.name || company
Party billingHub + deep links + statement; shared Pest PartyBilling suite; Playwright company case in contacts.party-billing.spec.ts

Tests

bash
# Backend
php artisan test --compact tests/Feature/Tenant/Company/CompanyTest.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:companies
SuiteLocation
Pesttests/Feature/Tenant/Company/CompanyTest.php; PartyBilling suite
E2Ee2e/tests/companies/; company hub smoke in contacts.party-billing.spec.ts

Logging

  • Spatie LogsActivity on Company (log name companies)
  • Domain company_activities timeline
  • PlatformAuditService via CompanyEventSubscriber

Intentional differences from Leads / Tasks

Leads / TasksCompanies
Stages / status workflowNo workflow — directory record
Follow-upsNone
Board viewList/table only
export / convert / completeNone

Deferred

  • Lead convert-to-Company
  • Legacy contact company string → Company backfill job
  • Meta invent Companies

Official documentation for the EloSync SaaS Platform.