Skip to main content
SHIPPING Q1 · 3 AI-NATIVE SaaS PRODUCTS300+ SALESFORCE PROJECTS DELIVERED15+ YEARS · TRUSTED IMPLEMENTATION PARTNERAI AGENTS · LLM · RAG · MLOPS · NOW HIRINGLIVE IN PRODUCTION ACROSS 3 INDUSTRIESSHIPPING Q1 · 3 AI-NATIVE SaaS PRODUCTS300+ SALESFORCE PROJECTS DELIVERED15+ YEARS · TRUSTED IMPLEMENTATION PARTNERAI AGENTS · LLM · RAG · MLOPS · NOW HIRINGLIVE IN PRODUCTION ACROSS 3 INDUSTRIES
FullServicePros logofullservicepros.net
Case Study · Restoration · In FlightActive development

FullServicePro: Restoration CRM with AI, In Flight

A white-labeled Frappe + Vue restoration CRM for a franchise operator.

Built around the way restoration franchises actually run: insurance-driven billing, multi-location crew dispatch, and an inline-edit experience that respects the way office and field teams really work. AI agent and in-app chatbot land next.

Restoration worksite in progress, representative imagery for the FullServicePro CRM build
In flight
Status: active development
Frappe 15
Python-native domain model
Vue 3.5 SPA
Composition API, script-setup
0 per-seat
Licensing fees
ScopeArchitecture · Backend (Frappe) · Frontend (Vue 3 SPA) · AI Agent (next) · Mobile Crew Views (next) · DevOps
01About the client

Industry: Multi-location restoration franchise operator, the kind of business that runs water, fire, mold, and contents jobs across dozens of locations, billed predominantly through insurance carriers and adjusters.

Why custom: Off-the-shelf restoration platforms (ServicePro, Albi, and the rest) charge per user per month and ship a fixed data model. For a franchise operator scaling crew headcount, the bill is the wrong shape and the workflow is the wrong fit.

Engagement: Long-running build with weekly demos. Designed to be handed off to an in-house team when the client's ready, or to run as managed services indefinitely. No platform lock-in. The source is theirs.

02The problem

Why off-the-shelf restoration software keeps falling short.

Restoration franchises have data models that off-the-shelf platforms can't bend to: multi-location with shared customers, insurance-driven billing with state-by-state rules, mobile-first crews who don't sit at desks.

Field crew on a restoration job, the workflow generic field-service apps don't fit
ServicePro / Albi per-seat tax
A 50-location franchise pays for every user; the bill scales with crew size, not value.
Off-the-shelf platforms can't fit restoration billing
ACORD forms, Xactimate-style estimates, deductibles, carrier-specific paperwork, sublimits. Generic field-service platforms model none of it.
Multi-location operators have no single source of truth
Franchise meetings run off Google Sheets exported from three different systems.
Crews in the field lack good mobile workflows
Status updates happen via WhatsApp; photos live in someone's camera roll; office spends afternoons reconciling.
Mid-build platform pivots are expensive
Most restoration CRMs are coupled tightly to one workflow shape, so small process changes mean big rebuilds.
03Engineering principles

Six rules that shaped the build.

01

Frappe for the data model

Python-native DocTypes give us schema we can shape directly. Restoration's data is multi-location, insurance-driven, and mobile-first, and it fights every off-the-shelf platform. Frappe doesn't.

02

Vue 3 SPA for the UX

We're not living inside Frappe's default desk UI. A separate Vue 3 + Vite + Tailwind frontend gives us control over every inline-edit affordance, every dark-mode tile, every keyboard shortcut.

03

Three-tier API, always

Thin @frappe.whitelist() handlers call service-layer functions that contain the business logic. The DB is the only thing the service layer touches directly. Tests stay focused; refactors stay safe.

04

Composables over copy-paste

useApi, useForm, useTable, useToast, useModal: every page consumes them, no page reimplements them. New screens take hours, not days.

05

Nothing hardcoded

All enums, badge styles, dropdown options, and theme values live in constants/ and CSS custom properties. A status-label rename never requires touching pages.

06

Permissions at the DB layer

Frappe RBAC plus explicit permission checks at every API call. Never ignore_permissions=True without a documented reason. Audit logs on every destructive action.

04What we're shipping

Eight modules. One coherent system.

01

Lead pipeline

FSP Lead with status workflow, source tracking, assignment, and conversion-to-Account/Job. Filters and saved views built in. No per-user CSV exports.

02

Accounts & Contacts

FSP Account and FSP Contact linked through the LinkLookup primitive with inline edit, Ctrl-click to new tab, and quick-create from any form. The book of business stays in one place.

03

Job dispatch

FSP Job with status lifecycle, crew assignment, service-line breakdown, attachments, activity timeline. Mobile-first crew views ship next.

04

Service catalog

FSP Service covers restoration service lines (water, fire, mold, contents) with pricing rules. Reusable across leads, jobs, and invoices.

05

Activity timeline

Every record carries an immutable activity log. Notes, status changes, attachments, and system events show up as one chronological feed per Account, Lead, and Job.

06

Calendar

Dispatch calendar showing scheduled jobs across crews. The view ops actually opens in the morning.

07

Financial reports

Five live reports: Financials, Outstanding (AR aging), Jobs, Leads, Team. Built on the service layer, paginated at the DB level. No Python-side filtering of result sets.

08
In flight

AI agent + chatbot, in flight

Active development. Lead-intake agent for first-notice-of-loss calls, plus an in-app chatbot for crew status updates and office Q&A over the Account / Job graph. Shares the agentic patterns from our other AI builds.

05UX details we care about

Why it feels good to use.

A franchise CRM lives or dies on small affordances: how fast you can find a contact, whether you have to leave the page to fix a typo, whether you can open a related job in a new tab the way you would on any modern site. These are the details we agonize over.

LinkLookup: inline edit, navigation, and quick-create in one primitive

Every reference to another DocType (Account, Contact, User) uses the same component. Hover for a pencil icon to edit in place. Ctrl/Cmd+click opens the linked record in a new tab. "+ New" opens a modal that creates the related record without losing your current form state. Used on every detail page and form across the app.

Saved filter views per list

Backed by saved_filter_service and list_view_service. Power users carry their own working set across sessions. No CSV-as-state.

Dark mode from day one

[data-theme="dark"] selector with CSS custom properties. Branded for the franchise, not a forced palette.

Standardized response envelopes

success_response, error_response, paginated_response, validation_error_response. Every API call returns the same shape. Frontend composables can rely on it.

Salesforce import path

import_salesforce.py: a documented migration route for franchises moving off Salesforce or other legacy CRMs. The architecture sprint includes a data-mapping deliverable.

06Architectural decisions

Four decisions that earned their footprint.

01

Why Frappe over Salesforce

Salesforce's per-user pricing kills the unit economics for a franchise with 50+ crew. Frappe is open-source, self-hostable, and has a Python-native domain model that bends to restoration's data instead of fighting it.

02

Why Vue 3 over Frappe Desk

Frappe's default desk UI is built for back-office data entry. Restoration franchise ops need a customer-facing-grade interface with inline edit, fast list views, and a branded look. A separate Vue SPA on top of Frappe's API gives us both.

03

Why a separate service layer

Business logic doesn't belong in @frappe.whitelist() handlers, where it can't be tested without HTTP. Service functions are pure Python: easy to test, easy to compose, easy to expose through admin scripts later.

04

Why composables over Pinia for everything

Pinia owns auth + settings (cross-cutting global state). Data fetching, forms, tables, and toasts each have one composable that pages call. State stays local where it should be.

07Roadmap

What lands next.

Dashboard view, the next surfaces in the build
Shipping next
  • Crew mobile views: offline-first job updates, photo uploads from site
  • AI lead-intake agent (FNOL): handles inbound calls, fills the lead record
  • In-app chatbot: "what's the status of job FSP-JOB-00214?" answered without leaving the page
After that
  • Invoicing module: ACORD-aware paperwork, carrier-specific billing, Xactimate-format export
  • Franchise HQ dashboard: throughput, margin, and utilization by location
  • Carrier portal integrations where carrier APIs exist
08Tech stack at a glance

What it runs on.

LayerTechnology
BackendFrappe 15 · Python · Service-layer architecture
DocTypesFSP Lead · FSP Account · FSP Contact · FSP Job · FSP Service · FSP Claim
FrontendVue 3.5+ (script-setup) · Vite 6 · TypeScript · Pinia
UITailwind CSS 3.4 · frappe-ui · Feather Icons · CSS custom properties
Data accessuseApi · useForm · useTable composables · standardized response helpers
Reports5 reports: Financials · Outstanding · Jobs · Leads · Team
AI (in flight)Lead-intake agent · in-app chatbot · activity-graph Q&A
DevOpspre-commit (ruff · eslint · prettier · pyupgrade) · Frappe bench
Next step

Running a restoration franchise on rented software?

If ServicePro fees scale faster than your margins, or your billing flow doesn't match ACORD/Xactimate reality, that's where this kind of build pays back. Start a conversation. we'll tell you honestly whether the ROI works at your scale.