Skip to content

Upgrade Guide ​

How to ship application releases to existing production installations without reseeding.

Standard upgrade ​

bash
php artisan down --retry=60   # optional
git pull
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan optimize
php artisan queue:restart
php artisan reverb:restart   # if Reverb is running
php artisan up

That is the complete path for catalog modules and tenant permission vocabulary changes that follow the platform pattern.

Documents 1.6.0 → 1.7.0 — nested folders + version history ​

  1. Deploy Backend and run php artisan migrate --force (document_folders, document_versions, documents.folder_id, catalog bump — do not db:seed)
  2. Confirm catalog version: documents 1.7.0
  3. Deploy Frontend after production build; hard-refresh SPA
  4. Existing documents stay unfiled until assigned to a folder; prior replaces had no history

Go-live: Documents folders + versions production readiness.

Expenses 1.6.0 → 1.7.0 — itemized lines ​

  1. Deploy Backend and run php artisan migrate --force (expense_lines table and catalog bump — do not db:seed)
  2. Confirm catalog version: expenses 1.7.0
  3. Deploy Frontend after production build; hard-refresh SPA. Ship the mobile build
  4. Existing expenses stay header-only until the next draft save

Go-live: Expense itemized lines production readiness.

Expenses 1.5.0 → 1.6.0 — undo payment ​

  1. Deploy Backend and run php artisan migrate --force (catalog bump only — do not db:seed)
  2. Confirm catalog version: expenses 1.6.0
  3. Deploy Frontend after production build; hard-refresh SPA. Ship the mobile build
  4. Existing paid expenses stay paid until someone undoes the payment

Go-live: Expense unpay production readiness.

Assets 1.2.0 → 1.3.0 / Accounting 1.9.0 → 1.10.0 — depreciation journals ​

  1. Deploy Backend and run php artisan migrate --force (asset depreciation columns, asset_depreciations, assets.depreciate, soft dependency, catalog bumps — do not db:seed)
  2. Confirm catalog versions: assets 1.3.0, accounting 1.10.0. Admin and manager have assets.depreciate
  3. Deploy Frontend after production build; hard-refresh SPA. Ship the mobile build
  4. Existing assets are not depreciated until someone posts a month

Go-live: Asset depreciation journals production readiness.

Invoice edit requests table already exists (Forge migrate 42S01) ​

If deploy fails on 2026_10_08_171215_create_customer_invoice_edit_requests_table with Table 'customer_invoice_edit_requests' already exists:

  1. Prefer pulling the Backend fix that makes that migration idempotent (Schema::hasTable + short index cie_edit_req_tenant_invoice_status_idx), then re-run php artisan migrate --force.
  2. If you cannot deploy that fix first and the table is already correct, record the migration without re-running up() (migrate-only — do not db:seed / drop the table unless empty and unused):
sql
INSERT INTO migrations (migration, batch)
SELECT '2026_10_08_171215_create_customer_invoice_edit_requests_table',
       IFNULL(MAX(batch), 0) + 1
FROM migrations;

Then ensure the short composite index exists if missing:

sql
CREATE INDEX cie_edit_req_tenant_invoice_status_idx
  ON customer_invoice_edit_requests (tenant_id, customer_invoice_id, status);

Finally: php artisan migrate --force and retry Forge deploy.

Guest share PDF-parity preview — invoices 1.12.1 / quotations 1.10.1 ​

  1. Deploy Backend and run php artisan migrate --force (catalog bumps only — do not db:seed)
  2. Confirm catalog: invoices 1.12.1, quotations 1.10.1
  3. Deploy Frontend from build-artifacts after CI publishes — guest share pages show Branded logo chrome + full document preview
  4. Smoke: open an unpaid invoice share link with Branded installed (tenant logo) and without (EloSync logo); confirm issuer/bill-to/lines/notes match PDF download
  1. Deploy Backend and run php artisan migrate --force (public view token columns + catalog bumps — do not db:seed)
  2. Confirm catalog: invoices 1.12.0, quotations 1.10.0
  3. Deploy Frontend from build-artifacts (not main) after Frontend production build succeeds — Settings → Branding bank accounts; invoice/quotation Copy share link; guest /#/view/… pages. If Actions failed typecheck on invoice_bank_accounts, merge the share-link SPA publish fix first so artifacts update.
  4. Staging smoke: two banks / one disabled → send → share link → guest view + PDF → void/reject clears link

Go-live: Billing share + multi-bank production readiness.

Configurable digests — invoices 1.11.0 / departments 1.2.0 / attendance 1.8.0 ​

  1. Deploy Backend and run php artisan migrate --force (department_report_user + catalog bumps — do not db:seed)
  2. Confirm catalog: invoices 1.11.0, departments 1.2.0, attendance 1.8.0
  3. Deploy Frontend — Settings → Notifications invoice digest; department Report recipients
  4. Staging smoke: enable invoice digest + recipients; assign department report recipients; confirm attendance digests stay silent until both Settings toggles and recipients are set

Go-live: Configurable digests production readiness.

WhatsApp Cloud 1.5.0 → 1.6.0 (+ Help Desk → 1.18.0) — auto-ticket + note sync ​

  1. php artisan migrate --force (auto_create_help_desk_tickets, note/message FKs, catalog bumps — do not db:seed)
  2. Confirm catalog: whatsapp-cloud 1.6.0, help-desk 1.18.0
  3. Deploy Frontend — WhatsApp Auto-create Help Desk tickets toggle
  4. Staging smoke: entitle both modules → enable auto-create → inbound creates ticket; public note syncs inside 24h; internal notes stay local

Go-live: WhatsApp Help Desk bridge 1.6.0 production readiness.

Tasks 1.10.0 → 1.10.1 / Help Desk 1.17.0 → 1.17.1 — human-readable status timing ​

  1. php artisan migrate --force (do not db:seed)
  2. Confirm catalog: tasks 1.10.1, help-desk 1.17.1
  3. Smoke: task/ticket timing shows values like 3h / 1d 2h (not DD:HH:MM)

Help Desk 1.16.0 → 1.17.0 — status timing ​

  1. php artisan migrate --force (do not db:seed)
  2. Confirm catalog: help-desk 1.17.0
  3. Smoke: ticket Open → In Progress → Resolved/Closed → Timeline shows Created → Resolved; reopen clears cycle

Go-live: Help Desk 1.17.0 production readiness.

Help Desk 1.15.0 → 1.16.0 (+ WhatsApp Cloud 1.4.0 → 1.5.0) — multi-channel intake ​

  1. Deploy Backend and run php artisan migrate --force (whatsapp_conversations.help_desk_ticket_id + catalog bumps — do not db:seed)
  2. Confirm catalog: help-desk 1.16.0, whatsapp-cloud 1.5.0
  3. Deploy Frontend — WhatsApp Escalate + Help Desk source badges
  4. Staging smoke: entitle both modules → escalate conversation → ticket link + WhatsApp badge on Help Desk

Go-live: Help Desk 1.16.0 production readiness.

Customer Portal 1.2.0 → 1.3.0 (+ Help Desk 1.14.0 → 1.15.0) ​

  1. Deploy Backend and run php artisan migrate --force (portal magic-link + 2FA columns; catalog bumps — do not db:seed)
  2. Confirm catalog: customer-portal 1.3.0, help-desk 1.15.0
  3. Deploy Frontend (magic-link / Security 2FA / portal KB; Help Desk conversation polish; Settings Help Desk email toggles)

Go-live: Customer Portal 1.3.0 · Help Desk 1.15.0.

Help Desk 1.13.0 → 1.14.0 (+ Customer Portal 1.1.0 → 1.2.0) ​

  1. Deploy Backend and run php artisan migrate --force (help_desk_notes.is_internal + backfill + catalog bumps — do not db:seed)
  2. Confirm catalog: help-desk 1.14.0, customer-portal 1.2.0
  3. Deploy Frontend (Conversation vs Internal notes; portal TipTap replies + attachments) and Mobile (visibility toggle)

Go-live: Help Desk public replies 1.14.0 production readiness.

Customer Portal 1.0.0 (+ Help Desk 1.12.0 → 1.13.0) ​

  1. Deploy Backend and run php artisan migrate --force (registers customer-portal, permissions, portal_users / reset tokens, contacts hard dependency, Help Desk portal_user_id + catalog 1.13.0 — do not db:seed)
  2. Confirm catalog: customer-portal 1.0.0, help-desk 1.13.0
  3. Deploy the SPA — Operations → Customer Portal + /#/portal/* guest shell
  4. Staging smoke: install Contacts → Customer Portal → invite Contact → accept invite → login → invoice/ticket smoke when those modules are entitled

Go-live: Customer Portal 1.0.0 production readiness.

Payments 1.6.0 → 1.7.0 — live Stripe refunds ​

  1. Deploy Backend and run php artisan migrate --force (customer_payments.gateway_mode, customer_payment_refunds.stripe_refund_id, catalog 1.7.0 — do not db:seed)
  2. Confirm catalog: payments 1.7.0
  3. Deploy the SPA — posted Stripe payments show Refund via Stripe; other methods keep Record refund
  4. Stripe webhook for the active mode must include refund.created (Checkout events stay)
  5. Staging smoke: portal Pay now → posted gateway payment → partial Refund via Stripe in sandbox → Stripe refund id on the payment → invoice balance restored

Go-live: Payments Stripe refunds 1.7.0 production readiness.

Payments 1.5.0 → 1.6.0 / Customer Portal 1.3.0 → 1.4.0 — tenant Stripe portal Checkout ​

  1. Deploy Backend and run php artisan migrate --force (tenant_stripe_connections, gateway txn unique index, payments.manage_integrations, catalog bumps — do not db:seed)
  2. Confirm catalog: payments 1.6.0, customer-portal 1.4.0; admin/manager have payments.manage_integrations
  3. Deploy the SPA — Payments → Stripe; portal invoice Pay now
  4. Staging smoke: save sandbox keys → enable → test → configure Stripe webhook → portal Pay now → invoice paid

Go-live: Tenant Stripe portal checkout production readiness.

Calendar 1.9.0 → 1.10.0 — named-calendar provider sync/watches ​

  1. Deploy Backend and run php artisan migrate --force (calendar_provider_links, calendar_event_provider_mappings, bump to 1.10.0 — do not db:seed)
  2. Confirm catalog version: calendar 1.10.0
  3. Deploy the SPA — Manage calendars provider badges; Sync panel named-calendar copy
  4. Existing Google Calendar connections: Disconnect → Connect (full calendar scope required to create secondary calendars)
  5. Staging smoke: connect provider → create named calendar → synced badge → post event on named calendar → Sync now

Go-live: Calendar named-provider sync 1.10.0 production readiness.

Calendar 1.8.0 → 1.9.0 — named team/department calendars ​

  1. Deploy Backend and run php artisan migrate --force (calendars table, calendar_events.calendar_id, calendar.manage_calendars, bump to 1.9.0 — do not db:seed)
  2. Confirm catalog version: calendar 1.9.0; admin/manager have calendar.manage_calendars
  3. Deploy the SPA — Manage calendars, filter chips, event Calendar picker
  4. Staging smoke: create named calendar → post event → filter; with Departments entitled, department member sees team events

Go-live: Calendar named calendars 1.9.0 production readiness.

Calendar 1.7.0 → 1.8.0 — webhook-driven inbound sync ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_08_160000_bump_calendar_module_to_1_8_0 — catalog only; do not db:seed)
  2. Confirm catalog version: calendar 1.8.0
  3. Confirm public POST /webhooks/calendar-sync/{provider} is reachable; scheduler runs calendar:renew-provider-watches daily; Horizon calendar-sync; hourly pull remains
  4. Deploy the SPA — Sync panel “near real-time” copy / badge
  5. Staging smoke: Connect → Sync now → provider-side create appears as External without waiting for the hourly job

Go-live: Calendar webhook inbound 1.8.0 production readiness.

Calendar 1.6.0 → 1.7.0 — Project/Contact/Company overlay push ​

  1. Deploy Backend and run php artisan migrate --force (catalog bump to 1.7.0 — do not db:seed)
  2. Confirm catalog version: calendar 1.7.0
  3. Deploy the SPA — Sync panel copy lists Project/Contact/Company overlays
  4. Confirm Horizon calendar-sync
  5. Staging smoke (OAuth connected): Project dates / Contact or Company follow-up → provider calendar

Go-live: Calendar overlay push 1.7.0 production readiness.

Calendar 1.5.0 → 1.6.0 — two-way inbound sync ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_05_170000_bump_calendar_module_version_to_1_6_0 — catalog only; do not db:seed)
  2. Confirm catalog version: calendar 1.6.0
  3. Deploy the SPA — Calendar → Sync gains Sync now + two-way copy
  4. Confirm scheduler: calendar:pull-provider-events hourly; Horizon calendar-sync
  5. Staging smoke: Connect provider → Sync now → External events appear as view-only

Go-live: Calendar two-way inbound 1.6.0 production readiness.

AI Inventory triage tools 1.21.0 → 1.22.0 ​

After migrate (2026_10_17_155210_bump_ai_module_to_1_22_0):

  1. Confirm catalog ai.version is 1.22.0 (migrate-only; do not db:seed).
  2. No SPA deploy required for confirm UI (tool-agnostic); Frontend e2e covers propose→confirm product note.
  3. Smoke: entitle ai + products + warehouses + inventory → Ask EloSync get_product / get_low_stock / get_inventory_stats → propose product note → Confirm.

Go-live: AI Inventory triage production readiness · ops AI deployment.

Inventory 1.4.0 → 1.5.0 — moving-average valuation ​

  1. Deploy Backend and run php artisan migrate --force (unit_cost on stock_movements, catalog bump — do not db:seed)
  2. Confirm catalog version: inventory 1.5.0
  3. Deploy Frontend after production build; hard-refresh SPA
  4. Smoke: stock in with unit cost → confirm Avg cost / Value columns and Inventory value KPI; receive a PO and confirm product cost moves toward the line price

Go-live: Inventory moving-average 1.5.0 production readiness.

Inventory 1.3.0 → 1.4.0 / Purchase Orders → 1.7.0 — serial/lot PO receive + transfers ​

  1. Deploy Backend and run php artisan migrate --force (catalog bumps only — do not db:seed)
  2. Confirm catalog versions: inventory 1.4.0, purchase-orders 1.7.0
  3. Deploy Frontend after production build; hard-refresh SPA
  4. Smoke: receive a serial/lot-tracked PO with serials/lot → complete a warehouse transfer with serials/lot

Go-live: Inventory serial/lot receive + transfer 1.4.0 production readiness.

Products → 1.5.0 / Inventory → 1.3.0 — serial + lot tracking MVP ​

  1. Deploy Backend and run php artisan migrate --force (track_serials, track_lots, stock_serials, stock_lots, catalog bumps — do not db:seed)
  2. Confirm catalog versions: products 1.5.0, inventory 1.3.0
  3. Deploy Frontend after production build; hard-refresh SPA
  4. Smoke: enable Track serials or Track lots (zero on-hand) → stock in with serials/lot → confirm set-quantity adjust rejects that SKU

Go-live: Inventory serial/lot 1.3.0 production readiness.

Accounting 1.8.0 → 1.9.0 / Purchase Orders 1.5.0 → 1.6.0 — Pay vendor AP settle ​

  1. Deploy Backend and run php artisan migrate --force (receipt paid_from_account_id / payment_journal_entry_id, purchase-orders.pay grant, catalog bumps; do not db:seed)
  2. Confirm catalog versions: accounting 1.9.0, purchase-orders 1.6.0
  3. Deploy Frontend after production build; hard-refresh SPA
  4. Smoke: receive a tracked PO → Pay vendor from cash/bank → accounts payable open is zero

Go-live: Accounting vendor AP settle 1.9.0 production readiness.

Accounting 1.7.2 → 1.8.0 — purchase receipts and inventory journals ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_16_224416_add_journal_entry_id_to_purchase_order_receipts_and_stock_movements and 2026_10_16_224417_bump_accounting_module_version_to_1_8_0; do not db:seed)
  2. Confirm catalog version: accounting 1.8.0
  3. No SPA deploy is required for this bump
  4. Staging smoke: with Accounting installed, receive a tracked purchase order and confirm one posted journal (Dr 1200 / Cr 2000). Issue stock and confirm Dr 5000 / Cr 1200. Complete a warehouse transfer and confirm it adds no journal

Go-live: Accounting AP/COGS 1.8.0 production readiness.

Automation 1.8.0 → 1.9.0 — if/else branches ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_16_215505_create_automation_branches_and_link_steps and 2026_10_16_215506_bump_automation_module_to_1_9_0; do not db:seed)
  2. Confirm catalog version: automation 1.9.0
  3. Deploy the SPA — workflow builder gains Branches
  4. Restart Horizon so workers load the branch planner (automations queue)
  5. Staging smoke: save a workflow with an if branch and an otherwise branch; manual run with priority=high takes the if branch

Go-live: Automation branching 1.9.0 production readiness.

Automation 1.7.0 → 1.8.0 — generate_order (Purchase Order) ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_08_181000_bump_automation_module_to_1_8_0 — catalog only; do not db:seed)
  2. Confirm catalog version: automation 1.8.0
  3. Deploy the SPA — new Generate Purchase Order action (vendor_id, email/WhatsApp auto-send)
  4. Ensure Purchase Orders (+ Vendors) entitled for workspaces that use the action; Horizon automations / emails / whatsapp-outbound
  5. Staging smoke: Manual or Vendor trigger → draft PO; with auto_send → Sent + vendor email

Go-live: Automation generate_order 1.8.0 production readiness.

Automation 1.6.0 → 1.7.0 — WhatsApp document auto-send ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_08_180000_bump_automation_module_to_1_7_0 — catalog only; do not db:seed)
  2. Confirm catalog version: automation 1.7.0
  3. Deploy the SPA — Generate Quotation / Invoice gain Also send PDF on WhatsApp (gated by WhatsApp Cloud)
  4. Restart workers including whatsapp-outbound; WhatsApp Cloud platform connection must be ready
  5. Staging smoke: auto_send_whatsapp + open 24h conversation matched by bill-to phone → PDF document queued; closed window → failed run

Go-live: Automation WhatsApp document auto-send 1.7.0 production readiness.

Automation 1.5.0 → 1.6.0 — document auto-send ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_05_160000_bump_automation_module_version_to_1_6_0 — catalog only; do not db:seed)
  2. Confirm catalog version: automation 1.6.0
  3. Deploy the SPA — Generate Quotation / Invoice actions gain Auto-send checkbox
  4. Restart queue workers so billed document emails process (emails / mail queue)
  5. Staging smoke: workflow with auto_send + company email → Sent/Unpaid + mail; without email → failed run

Go-live: Automation document auto-send 1.6.0 production readiness.

Calendar 1.4.0 → 1.5.0 — Meeting/Task/Lead overlay provider push ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_05_153000_bump_calendar_module_version_to_1_5_0 — catalog only; do not db:seed)
  2. Confirm catalog version: calendar 1.5.0
  3. Deploy the SPA — Calendar → Sync copy mentions Meeting/Task/Lead overlays
  4. Confirm Horizon workers process the calendar-sync queue (php artisan queue:restart)
  5. Staging smoke (with OAuth connected): create Task with due / Lead follow-up / Meeting → provider calendar updates; clear due or cancel meeting → provider event cancelled

Go-live: Calendar overlay provider push 1.5.0 production readiness.

Leads 1.8.0 → 1.9.0 — inactivity manager digest ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_04_193718_bump_leads_module_to_1_9_0 — catalog only; do not db:seed)
  2. Confirm catalog version: leads 1.9.0
  3. Deploy the SPA — Settings → Leads inactivity controls
  4. Restart queue workers (php artisan queue:restart) so leads:notify-inactive mail jobs pick up the digest notification
  5. Confirm the scheduler already runs leads:notify-inactive daily (routes/console.php)

Projects 1.7.0 → 1.8.0 — real-time board sync ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_05_031200_bump_projects_module_to_1_8_0 — catalog only; do not db:seed)
  2. Confirm catalog version: projects 1.8.0
  3. Deploy the SPA after migrate — Projects Kanban board realtime (useProjectsBoardRealtime; Gantt/Heatmap stay poll-on-navigate)
  4. Confirm Reverb is running (php artisan reverb:restart if already up)
  5. Staging smoke: two browser sessions on the Projects Board — create, drag status, assign, delete

Go-live: Projects real-time board sync 1.8.0 production readiness.

Help Desk 1.11.0 → 1.12.0 — real-time board sync ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_05_023800_bump_help_desk_module_to_1_12_0 — catalog only; do not db:seed)
  2. Confirm catalog version: help-desk 1.12.0
  3. Deploy the SPA after migrate — Help Desk Kanban board realtime (useHelpDeskBoardRealtime)
  4. Confirm Reverb is running (php artisan reverb:restart if already up)
  5. Staging smoke: two browser sessions on the Help Desk board — create, drag status, assign, delete; the other session’s board + KPIs update without a manual refresh

Go-live: Help Desk real-time board sync 1.12.0 production readiness.

Tasks 1.7.0 → 1.8.0 — real-time board sync ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_04_193500_bump_tasks_module_to_1_8_0 — catalog only; do not db:seed)
  2. Confirm catalog version: tasks 1.8.0
  3. Deploy the SPA after migrate — Tasks Kanban board realtime (useTasksBoardRealtime)
  4. Confirm Reverb is running (php artisan reverb:restart if already up)
  5. Staging smoke: two browser sessions on the Tasks board — create, drag status, assign, delete; the other session’s board + KPIs update without a manual refresh

Go-live: Tasks real-time board sync 1.8.0 production readiness.

Document-field TipTap — announcements 1.2.0 / help-desk 1.11.0 / tasks 1.7.0 / projects 1.7.0 / activities 1.2.0 ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_03_182400_bump_document_rich_text_module_versions — catalog only; do not db:seed)
  2. Confirm catalog versions: announcements 1.2.0, help-desk 1.11.0, tasks 1.7.0, projects 1.7.0, activities 1.2.0
  3. Deploy the SPA after migrate — TipTap on those document fields; sanitized HTML on view/inbox
  4. Deploy Mobile (plain-text strip on those views) and Docs
  5. Staging smoke: published announcement with list/link/color → inbox + record HTML; dashboard snippet without tags; existing plain-text bodies still open

Go-live: Document-field TipTap production readiness.

Workspace record cohesion — contacts 1.6.0 / companies 1.3.0 / documents 1.6.0 / email 1.4.0 ​

  1. Deploy Backend and run php artisan migrate --force (2026_09_29_211500_bump_contacts_companies_documents_email_for_workspace_cohesion — catalog only; do not db:seed)
  2. Confirm catalog versions: contacts 1.6.0, companies 1.3.0, documents 1.6.0, email 1.4.0
  3. Deploy the SPA after migrate — Contact/Company related hubs, Email CRM links UI, party list chips, Documents ?contact= / ?company=
  4. Staging smoke: contact with linked opportunity/ticket/project/document → related sections + View all chips; Email link/unlink Contact; /documents?contact= filtered list

Go-live: Workspace record cohesion production readiness.

Projects 1.5.0 → 1.6.0 — workload heatmap ​

  1. Deploy Backend and run php artisan migrate --force (catalog bump only — do not db:seed)
  2. Deploy the SPA after migrate — Projects Heatmap view mode
  3. Staging smoke: open project with assignee → Heatmap row → week cells reflect schedule overlap; with Tasks entitled, due-this-week project tasks add to cell load; pressure band shows

Go-live: Projects Heatmap 1.6.0 production readiness.

Projects 1.4.0 → 1.5.0 — portfolio Gantt ​

After migrate (2026_09_28_220000_bump_projects_module_version_to_1_5_0):

  1. Confirm catalog projects version is 1.5.0 (migrate-only; do not db:seed)
  2. Deploy the SPA after migrate — Projects Gantt view mode
  3. Staging smoke: dated project → Gantt bar → expand milestones/tasks → drag dates (update permission); without Tasks entitled, task markers omitted

Go-live: Projects Gantt 1.5.0 production readiness.

Leads 1.5.0 → 1.6.0 — created_by / updated_by ​

After migrate (2026_09_26_112037_add_created_by_and_updated_by_to_leads_table, 2026_09_26_112040_bump_leads_module_to_1_6_0, 2026_09_26_133208_remediates_leads_created_by_updated_by_backfill):

  1. Confirm catalog leads version is 1.6.0 (migrate-only; do not db:seed)
  2. Confirm tenant leads has created_by / updated_by (nullable FKs); backfill is batched (chunk 500)
  3. Deploy the SPA after migrate — Created by / Updated by columns and filters
  4. Staging smoke: create → filter by creator → edit as second user → Updated by changes; add a note → Updated by changes; System filter for null actors

Go-live: Leads 1.6.0 created/updated by production readiness.

Live Chat 1.5.2 → 1.6.0 — department-routed notifications ​

  1. Deploy Backend and run php artisan migrate --force (2026_10_03_223500_add_live_chat_idle_routing_and_bump_to_1_6_0 — do not db:seed)
  2. Confirm catalog live-chat version is 1.6.0
  3. Confirm emails queue workers (or sync) process Live Chat notifications and that the scheduler runs live-chat:scan-idle every minute
  4. Deploy the SPA after migrate — Settings → Live Chat idle minutes; notification types live-chat.assigned / live-chat.idle
  5. Purge CDN for public/widgets/live-chat.js (snippet ?v=1.6.0)
  6. Staging smoke: department agents get first-chat bell + FCM; Staff outside the department do not; first agent reply claims the chat; idle minutes later alerts other department agents; visitor widget shows the answering agent’s Name

Live Chat 1.5.1 → 1.5.2 — inbound notification broadcast ​

After migrate (2026_09_25_210000_bump_live_chat_module_version_to_1_5_2):

  1. Confirm catalog live-chat version is 1.5.2
  2. Confirm emails queue workers (or sync) process LiveChatInboundMessageNotification
  3. Staging smoke: visitor send → agent open tab receives bell / NotificationCreated while Echo is connected

Live Chat 1.5.0 → 1.5.1 — production-readiness remediations ​

After migrate (2026_09_25_200000_bump_live_chat_module_version_to_1_5_1):

  1. Confirm catalog live-chat version is 1.5.1
  2. Confirm API serves updated public/widgets/live-chat.js; Settings embed snippet includes ?v=1.5.1 (purge CDN for unversioned URLs)
  3. Staging smoke: visitor send + open/close without HTTP 500 when automation/Reverb misbehaves; Retry does not duplicate a already-persisted visitor message

Go-live: Live Chat 1.5.0 / 1.5.1 production readiness.

Live Chat 1.4.2 → 1.5.0 — send soft-fail + typing ​

After migrate (2026_09_25_142200_bump_live_chat_module_version_to_1_5_0):

  1. Confirm catalog live-chat version is at least 1.5.0 (prefer 1.5.1 if that migration is present)
  2. Confirm API serves the updated static public/widgets/live-chat.js (purge CDN if used)
  3. Deploy the SPA after Backend — desk typing API + / canned filter
  4. php artisan reverb:restart when Reverb is used (typing/live desk); send works even if Reverb is down
  5. Staging smoke: third-party Origin visitor send + agent reply without HTTP 500

Go-live: Live Chat 1.5.0 / 1.5.1 production readiness. Baseline CORS/session audit: Live Chat production readiness.

User security settings (2FA, passkeys, sessions) ​

Deploy Backend + Frontend + Docs together. Run migrations before serving the SPA build that includes Profile → Security and the login TOTP step.

MigrationPurpose
2026_09_01_230027_add_session_metadata_to_personal_access_tokens_tableSession list metadata on Sanctum tokens
2026_09_01_231207_create_passkeys_tableWebAuthn passkeys
2026_09_01_231209_add_two_factor_columns_to_users_tableTOTP columns (tenant users)
2026_09_01_231400_add_two_factor_columns_to_central_users_tableTOTP columns (central users)

Set PASSKEYS_RELYING_PARTY_ID and PASSKEYS_ALLOWED_ORIGINS before enabling passkeys in production. See User security settings production readiness.

What migrate does for modules & RBAC ​

Change typeMechanismSafe for existing data?
New default-included moduleData migration + DefaultModuleRegistrarYes — insert-only catalog; install only if workspace never had a subscription row
New permissionsData migration + TenantPermissionSynchronizerYes — additive grants only; never resets customized roles
SchemaNormal Laravel migrationsFollow usual migration discipline

Do not run in production upgrades ​

  • php artisan db:seed
  • CatalogSeeder / Central catalog seeders to “pick up” new modules
  • Role/permission seeders to “sync” RBAC
  • Manual SQL that reactivates cancelled module subscriptions
  • Any process that expects login to repair missing permissions

Tenant API & Webhooks (Settings → Developers) ​

Migrate-first platform capability (not a Marketplace module).

  1. Deploy Backend + Frontend + Docs together.
  2. Run php artisan migrate --force — adds personal_access_tokens.token_type, tenant_webhook_endpoints, tenant_webhook_deliveries, and grants settings.manage_developers to owner/admin defaults.
  3. Ensure a queue worker processes the webhooks queue (DeliverTenantWebhookJob). Include webhooks in the Forge worker list (see Laravel Forge).
  4. Scheduler runs webhooks:prune-deliveries --days=90 weekly (delivery payload retention).
  5. Smoke: Settings → Developers → create API token → Bearer call → create webhook → Send test.

See Tenant API & Webhooks production readiness.

Deploy Backend + Frontend + Docs together.

  1. php artisan migrate --force — widens short_links.code to 64 chars; bumps catalog short-links to 1.2.0 (2026_08_23_231224_widen_short_link_codes_and_bump_version).
  2. Set production env: SHORT_LINK_BASE_URL (e.g. https://go.elosync.com), SHORT_LINK_MARKETING_URL, SHORT_LINK_BETA_URL.
  3. Point short-domain DNS at the same Laravel app that serves GET /r/{identifier}.
  4. Confirm queue workers process RecordShortLinkClickJob (default queue).
  5. Deploy SPA after migrate — vanity slug create field and copy icons require the new frontend build.
  6. Smoke: create vanity slug → public redirect; pause/delete → branded 404; npm run test:e2e:short-links.

Go-live: Short Links production readiness · ops Short Links deployment.

Platform polish lanes A, B, C ​

Deploy Backend + Frontend + Docs together. Frontend PR #143 is already on main; Backend PR #148 and Docs PR #170 must merge before production.

  1. php artisan migrate --force — attachment tables, document_links, catalog version bumps (2026_08_23_*).
  2. Deploy Backend before or with the SPA (receipt upload, document links, credit note / PO PDF+email APIs).
  3. Confirm emails queue workers and Storage upload disk (Storage production readiness).
  4. Smoke: documents, expenses, help-desk, tenant-dashboard, AI module Playwright scripts (--workers=1).

Go-live audit: Platform polish A/B/C production readiness — No-Go until Backend/Docs PRs merge.

AI Assistant platform 1.0.0 → 1.0.1 ​

After migrate (2026_08_21_010000–010300):

  1. Confirm catalog rows ai (billable 1.0.1), ai-credits-1k, ai-credits-5k, ai-credits-20k are published.
  2. Confirm permissions ai.use, ai.manage, ai.confirm exist and default admin/manager maps include them.
  3. Confirm scheduler runs ai:rollover-monthly-credits (daily, overlapping locked, one server).
  4. Set Central Settings → AI (ai_enabled, provider, encrypted ai_api_key, models, monthly included credits).
  5. Deploy the SPA after migrate — Ask EloSync / Settings AI (ai.manage) / Lead Copilot require the module entitlement.
  6. Smoke: entitle ai → wallet grant → Ask EloSync → Lead Copilot; platform mode returns 402 when credits are exhausted or below the pre-provider ceiling.

1.0.1 hardens credit integrity (wallet lockForUpdate, pre-provider credit ceiling, request-path ensurePeriod), enforces ai.manage on Settings AI, and adds throttle:ai on message/copilot routes.

Go-live: AI production readiness · ops notes AI deployment.

AI workspace search 1.2.0 → 1.3.0 ​

After migrate (2026_08_31_200000_bump_ai_module_version_to_1_3_0):

  1. Confirm catalog ai.version is 1.3.0 (migrate-only; do not db:seed).
  2. Deploy SPA + Mobile after Backend — Search workspace starter + Wave A+B+C citations.
  3. Smoke: Ask EloSync → Search workspace starter; confirm hits respect module entitlement and *.view; citations stay same-app.

Go-live: AI workspace search production readiness · ops AI deployment.

AI Help Desk triage tools 1.3.0 → 1.4.0 ​

After migrate (2026_09_15_040000_bump_ai_module_version_to_1_4_0):

  1. Confirm catalog ai.version is 1.4.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + help-desk → Ask EloSync open tickets → propose status/assign/note → Confirm; deny resolve without help-desk.close.

Go-live: AI Help Desk triage production readiness · ops AI deployment.

AI Task triage tools 1.4.0 → 1.5.0 ​

After migrate (2026_09_15_050000_bump_ai_module_version_to_1_5_0):

  1. Confirm catalog ai.version is 1.5.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai (Tasks is default-included) → Ask EloSync overdue/due-today tasks → propose status/assign/note → Confirm; deny complete without tasks.complete.

Go-live: AI Task triage production readiness · ops AI deployment.

AI Opportunity triage tools 1.5.0 → 1.6.0 ​

After migrate (2026_09_15_060000_bump_ai_module_version_to_1_6_0):

  1. Confirm catalog ai.version is 1.6.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + opportunities → Ask EloSync list stages / fetch opportunity → propose stage/assign/note → Confirm; deny stage without opportunities.update.

Go-live: AI Opportunity triage production readiness · ops AI deployment.

AI Invoice triage tools 1.6.0 → 1.7.0 ​

After migrate (2026_09_15_070000_bump_ai_module_version_to_1_7_0):

  1. Confirm catalog ai.version is 1.7.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + invoices → Ask EloSync fetch invoice / overdue → propose status/assign/note → Confirm; Draft→Unpaid needs invoices.send; cancel needs invoices.void.

Go-live: AI Invoice triage production readiness · ops AI deployment.

AI Expense triage tools 1.7.0 → 1.8.0 ​

After migrate (2026_09_15_080000_bump_ai_module_version_to_1_8_0):

  1. Confirm catalog ai.version is 1.8.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + expenses → Ask EloSync fetch expense / pending approval → propose status/assign/note → Confirm; approve needs expenses.approve; reject needs expenses.reject; paid via status needs expenses.pay (and paid_from_account_id on the expense when Accounting is entitled).

Go-live: AI Expense triage production readiness · ops AI deployment.

AI Project triage tools 1.8.0 → 1.9.0 ​

After migrate (2026_09_15_090000_bump_ai_module_version_to_1_9_0):

  1. Confirm catalog ai.version is 1.9.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + projects → Ask EloSync fetch project → propose status/assign/note → Confirm; status/note need projects.update; assign needs projects.assign.

Go-live: AI Project triage production readiness · ops AI deployment.

AI Purchase Order triage tools 1.9.0 → 1.10.0 ​

After migrate (2026_09_15_100000_bump_ai_module_version_to_1_10_0):

  1. Confirm catalog ai.version is 1.10.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + purchase-orders → Ask EloSync fetch PO → propose status/assign/note → Confirm; Draft→Sent needs purchase-orders.send; receive needs purchase-orders.receive; cancel needs purchase-orders.cancel.

Go-live: AI Purchase Order triage production readiness · ops AI deployment.

AI Payment triage tools 1.10.0 → 1.11.0 ​

After migrate (2026_09_15_110000_bump_ai_module_version_to_1_11_0):

  1. Confirm catalog ai.version is 1.11.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + payments → Ask EloSync fetch payment → propose post/void/assign/note → Confirm; Posted confirm calls post() (payments.post); Void confirm calls void() (payments.void); Draft target rejected.

Go-live: AI Payment triage production readiness · ops AI deployment.

AI Lead assign + note tools 1.11.0 → 1.12.0 ​

After migrate (2026_09_15_120000_bump_ai_module_version_to_1_12_0):

  1. Confirm catalog ai.version is 1.12.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + leads → Ask EloSync fetch lead → propose assign/note → Confirm; assign needs leads.assign; note needs leads.update.

Go-live: AI Lead assign + note production readiness · ops AI deployment.

AI Estimate triage tools 1.12.0 → 1.13.0 ​

After migrate (2026_09_16_010000_bump_ai_module_version_to_1_13_0):

  1. Confirm catalog ai.version is 1.13.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + estimates → Ask EloSync fetch estimate → propose status/assign/note → Confirm; Sent needs estimates.send; Accepted needs estimates.accept.

Go-live: AI Estimate triage production readiness · ops AI deployment.

AI Quotation triage tools 1.13.0 → 1.14.0 ​

After migrate (2026_09_16_020000_bump_ai_module_version_to_1_14_0):

  1. Confirm catalog ai.version is 1.14.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + quotations → Ask EloSync fetch quotation → propose status/assign/note → Confirm; Sent needs quotations.send; Accepted needs quotations.accept.

Go-live: AI Quotation triage production readiness · ops AI deployment.

AI Credit Note triage tools 1.14.0 → 1.15.0 ​

After migrate (2026_09_16_030000_bump_ai_module_version_to_1_15_0):

  1. Confirm catalog ai.version is 1.15.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + credit-notes (Invoices required) → Ask EloSync fetch credit note → propose status/assign/note → Confirm; Issued needs credit-notes.issue; Draft target rejected.

Go-live: AI Credit Note triage production readiness · ops AI deployment.

AI Leave triage tools 1.15.0 → 1.16.0 ​

After migrate (2026_09_16_040000_bump_ai_module_version_to_1_16_0):

  1. Confirm catalog ai.version is 1.16.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + employees + leave-management → Ask EloSync list pending / fetch leave → propose approve or reject → Confirm; both writes need leave-management.approve; reject requires review notes.

Go-live: AI Leave triage production readiness · ops AI deployment.

AI Contract triage tools 1.16.0 → 1.17.0 ​

After migrate (2026_09_27_180000_bump_ai_module_version_to_1_17_0):

  1. Confirm catalog ai.version is 1.17.0 (migrate-only; do not db:seed).
  2. No SPA/Mobile deploy required for this bump (confirm UI is tool-agnostic).
  3. Smoke: entitle ai + opportunities + contracts → Ask EloSync fetch contract → propose status/assign/note → Confirm; Sent needs contracts.send (issues acceptance token); Active from Sent needs contracts.accept.

Go-live: AI Contract triage production readiness · ops AI deployment.

Sales document invoice conversion (quotations 1.4.1 / contracts 1.2.1 / invoices 1.6.1 / estimates 1.3.3) ​

After migrate:

  1. Confirm catalog versions: quotations 1.4.1, contracts 1.2.1, invoices 1.6.1, estimates 1.3.3
  2. Confirm customer_invoices.contract_id exists (nullable FK) and unique nullable estimate_id
  3. Confirm quotations.convert and contracts.convert are granted to default admin/manager roles
  4. Deploy the SPA after migrate — convert actions 422 until Invoices is entitled; contract re-bills need acknowledge_repeat_billing

Do not unique-index customer_invoices.quotation_id. Contract billing is repeatable. Quote/estimate one-shot uses QuotationInvoiceGuard plus lockForUpdate (and unique estimate_id).

Go-live: Sales document convert production readiness.

Sales document invoice conversion (quotations 1.4.0 / contracts 1.2.0 / invoices 1.6.0 / estimates 1.3.2) ​

Superseded by 1.4.1 / 1.2.1 / 1.6.1 / 1.3.3 integrity hardening above. Historical checklist:

  1. Confirm catalog versions: quotations 1.4.0, contracts 1.2.0, invoices 1.6.0, estimates 1.3.2
  2. Confirm customer_invoices.contract_id exists (nullable FK)
  3. Confirm quotations.convert and contracts.convert are granted to default admin/manager roles
  4. Deploy the SPA after migrate — convert actions 422 until Invoices is entitled on the workspace

Do not unique-index customer_invoices.quotation_id. Contract billing is repeatable; the quotation/estimate one-shot guard is application-level (QuotationInvoiceGuard).

Calendar invitee ACL 1.1.0 → 1.2.0 ​

After migrate (2026_09_27_190000_bump_calendar_module_version_to_1_2_0):

  1. Confirm catalog calendar.version is 1.2.0 (migrate-only; do not db:seed).
  2. Deploy Backend before SPA — invitees need list/view ACL + read_only on the resource.
  3. Smoke: create a meeting with an internal invitee → invitee GET /calendar/events includes the projection with read_only: true; PUT returns 403.

Go-live: Calendar invitee ACL 1.2.0 production readiness.

Contracts 1.6.0 → 1.7.0 — accept evidence ​

After migrate (2026_09_24_020619_add_contract_acceptance_evidence_fields_to_contracts_table):

  1. Confirm catalog contracts version is 1.7.0
  2. Confirm nullable columns: accepted_by_phone, acceptance_user_agent, acceptance_signature_path, acceptance_signature_method, acceptance_id_document_path, acceptance_id_document_type, acceptance_id_document_original_name
  3. Deploy the SPA after migrate — Settings toggles and guest accept multipart fields
  4. Signature/ID Settings keys require Storage entitlement (phone does not); SPA switches stay disabled without Storage
  5. Smoke public accept + staff signature/ID downloads + PDF signature embed + force-delete purge on staging

Do not ship the empty stub 2026_09_23_210611_add_contract_acceptance_evidence_fields_to_contracts_table if it appears in a branch — only the 2026_09_24_020619_… migration is valid.

Go-live: Contracts 1.7.0 accept evidence production readiness.

Contracts auto-renew 1.7.0 → 1.8.0 ​

After migrate (2026_09_27_142116_add_auto_renew_to_contracts_table + 2026_09_27_142147_bump_contracts_module_version_to_1_8_0):

  1. Confirm catalog contracts.version is 1.8.0 (migrate-only; do not db:seed).
  2. Confirm contracts.auto_renew exists (boolean, default false).
  3. Confirm scheduler runs contracts:process-auto-renewals daily (Forge/Cloud).
  4. Deploy SPA after migrate — Auto-renew checkbox/Switch and validation.

Go-live: Contracts auto-renew 1.8.0 production readiness.

Contracts 1.1.0 — auto-fill and HTML memos ​

After migrate:

  1. Confirm catalog contracts version is 1.1.0
  2. Confirm contracts.description exists (nullable text)
  3. Deploy the SPA after migrate — posting description before the column exists will 500
  4. Store allows the creating actor as assigned_to. The SPA copies assignee only when that user is in the eligible picker; omitted assigned_to still defaults to the actor.

See Contracts 1.1.0 production readiness.

Founding Beta invites ​

After migrate (2026_08_16_232506_add_founding_beta_invites_to_beta_applications_table):

  1. Confirm founding_beta_enabled, founding_beta_apply_url, and founding_beta_invite_ttl_days exist (seeded via updateOrInsert in that migration).
  2. Ensure a queue worker listens to emails (Founding Beta invite notification).
  3. Set FRONTEND_URL to the SPA that serves /#/register?invite=….
  4. Keep marketing origins off SANCTUM_STATEFUL_DOMAINS (public apply/resend use Bearer-less JSON + CSRF except).

Go-live checklist: Founding Beta invite production readiness.

Invoices 1.2.0 (status model) ​

After migrate:

  1. Confirm catalog invoices version is 1.2.0
  2. Confirm existing invoice rows remapped (sent/partial → unpaid, void → cancelled)

Invoices 1.1.1 (branded PDF settings) ​

After migrate:

  1. Confirm catalog invoices version is 1.1.1
  2. Operators can fill Settings → Branding → Invoice company profile / payment details for PDF chrome

Invoices 1.1.0 (recurring + PDF) ​

After migrate:

  1. Confirm catalog invoices version is at least 1.1.0 (prefer 1.1.1 after the branded PDF polish migrate)
  2. Confirm scheduler includes invoices:generate-recurring (daily, withoutOverlapping(120), onOneServer)
  3. composer install must include dompdf/dompdf for PDF download
  4. Optional env (defaults are fine): INVOICES_RECURRING_CATCHUP_CAP (52), INVOICES_RECURRING_CHUNK_SIZE (100), INVOICES_RECURRING_TIME_BUDGET_SECONDS (45), INVOICES_PDF_CACHE_SECONDS (300), INVOICES_PDF_PER_MINUTE (30)
  5. Treat a non-zero invoices:generate-recurring exit as a tenant-series failure — see logs invoices.generate-recurring.tenant_failed

See Invoices 1.1.0 production readiness.

Billing product line picker (quotations / estimates / invoices + products) ​

After migrate (Backend before SPA):

bash
php artisan migrate --force
# includes:
# 2026_08_18_000658_bump_products_module_version_to_1_1_0
# 2026_08_18_001042_add_product_id_to_billing_document_lines_tables
# 2026_08_18_001043_bump_billing_document_modules_for_product_line_picker
# 2026_08_18_063000_bump_modules_for_product_line_picker_hardening
  1. Confirm catalog versions: products 1.1.1, quotations 1.3.1, estimates 1.3.1, invoices 1.5.1
  2. Existing entitled workspaces keep entitlements; catalog bump does not auto-install modules
  3. Smoke: create a line with product_id on a draft quotation/estimate/invoice; convert estimate → invoice; generate a recurring occurrence

See Billing product line picker production readiness.

Storage module (2026-08-13) ​

After migrating Storage:

  1. Map gateway prices for each billable pack (storage-10 … storage-1000) × monthly/yearly
  2. Confirm FILESYSTEM_BRANDING_DISK=public and uploads on S3/Wasabi
  3. Workspaces that already had Team Chat receive free Storage automatically (grandfather migration)
  4. New workspaces install free Storage from Marketplace when they need content uploads

See Storage deployment.

New workspaces after upgrade ​

TenantProvisioningService continues to:

  1. Install every published is_default_included module
  2. Provision default roles/permissions via TenantAuthorizationProvisioningService
  3. Create the owner via TenantAuthBootstrapService (no RBAC mutation on later logins)

Multi-Provider Email Delivery ​

After pulling a release that includes multi-provider email:

bash
composer install --no-dev --optimize-autoloader   # pulls symfony/postmark-mailer + mailgun-mailer
php artisan migrate --force                       # email_logs tables + email-logs.* permission grants
php artisan email:migrate-tenant-mail-modes       # optional; --dry-run first. Backfills mail_mode from legacy mail_host
php artisan queue:restart                         # required — workers cache mailer config

Notes:

  • Legacy tenants with a filled mail_host still behave as custom SMTP until the migrate command runs (recommended).
  • Configure Central Settings → Mail (SMTP / Postmark / Mailgun). Env MAIL_* remains bootstrap fallback only.
  • After changing mail credentials in Settings, always run php artisan queue:restart.
  • Smoke: Central + Tenant Send test, then open Email logs.

See Multi-Provider Email.

Verification ​

bash
php artisan test --compact tests/Feature/ProductionModuleDeploymentTest.php

Smoke:

  1. Existing workspace receives the new module in nav only if it never had that subscription (or already had it active)
  2. Customized role permission sets are unchanged except for explicitly migrated additive grants
  3. New registration still gets all default-included modules + full owner permissions

Expenses categories 1.1.0 (first-party SPA contract) ​

Ship Backend migrate before the SPA. Expense resources no longer return category as a string; the SPA expects category_id and embedded { id, name, slug }.

bash
php artisan migrate --force   # expense_categories + backfill + catalog bump 1.0.0 → 1.1.0
# then deploy Frontend, then Docs

Do not run db:seed. Starter categories (Travel / Office / Software / Utilities / Other) lazy-seed on first list/create/PO convert. Other cannot be deleted; starter slugs stay stable if renamed.

See Expenses production.

Help Desk module v1.0.0 ​

After migrate, existing workspaces do not auto-install Help Desk. Operators enable help-desk from Marketplace. Registration is migrate-only via DefaultModuleRegistrar — do not run db:seed. Permissions ship additively via TenantPermissionSynchronizer.

bash
php artisan migrate --force   # help_desk_* tables + catalog registration + help-desk.* permissions
# then deploy Frontend, then Docs

See Help Desk production.

Projects 1.0.0 + Tasks project_id (1.2.0) ​

bash
php artisan migrate --force   # projects tables + permissions + catalog + tasks.project_id + tasks 1.2.0
# then deploy Frontend (Projects nav + optional task project picker), then Docs

Do not run db:seed. Projects is free Marketplace opt-in (not default-included). See Projects production.

Automation module (billable add-on) ​

After migrate, existing workspaces do not auto-install Automation. Operators install automation from Marketplace. Include the automations queue on workers and confirm automation:dispatch-schedules is on the scheduler. Optional env: AUTOMATION_WEBHOOK_SECRET. See Automation production.

Storage module + capacity packs ​

After migrate, free storage and billable packs are catalog-only except: workspaces that already have Team Chat entitled receive free Storage via the grandfather migration (and new Team Chat installs companion-install Storage). Map Stripe/Creem prices for each pack × cycle before selling packs. See Storage deployment.

Knowledge Base module (free Operations opt-in) ​

After migrate, existing workspaces do not auto-install Knowledge Base. Operators install knowledge-base from Marketplace (internal articles only; not billable). See Knowledge Base production.

Assets module v1.0.0 ​

After migrate, existing workspaces do not auto-install Assets. Operators enable assets from Marketplace. Registration is migrate-only via DefaultModuleRegistrar — do not run db:seed. Permissions ship additively via TenantPermissionSynchronizer. No hard module dependencies.

bash
php artisan migrate --force   # assets / asset_notes / asset_activities + catalog registration + assets.* permissions
# then deploy Frontend, then Docs

See Assets production.

Official documentation for the EloSync SaaS Platform.