Skip to main content

Quickstart guide

This guide walks you through the complete inspection workflow, from setting up your company information to generating a signed final report.

Prerequisites

  • User account with Technician or Admin role
  • At least one PDF inspection form template uploaded
  • Basic familiarity with fire inspection procedures

Step 1: Configure company settings

Before conducting inspections, set up your contractor and license information. This data auto-fills into all inspection forms.
1

Navigate to company settings

Access the settings page at /settingsYou’ll see sections for:
  • Contractor information
  • License details
  • System categories
  • Inspection intervals
2

Add contractor information

Create a new contractor record with your company details:
# ContractorInfo model fields:
# - name: Company name
# - address: Business address
# - phone: Contact number
# - email: Business email
Navigate to /contractor_infos/new and fill in:
  • Company name (e.g., “FireMex Solutions”)
  • Business address
  • Contact phone and email
3

Add license information

Add your fire protection contractor license:
# LicenseInfo model fields:
# - license_number: State license number
# - issued_date: License issue date
# - expiration_date: License expiration
Navigate to /license_infos/new and enter:
  • License number (e.g., “C16-123456”)
  • Issue and expiration dates
The HeaderAutoFillerService automatically populates these details into every inspection form you create.

Step 2: Create a customer and property

Inspections are organized by customer properties. Set up your first customer and property.
1

Create a customer

Navigate to /customers/new
# Customer model:
class Customer < ApplicationRecord
  has_many :properties
  has_many :inspections, through: :properties
  
  scope :search_by_name, ->(query) { 
    where("name ILIKE ?", "%#{query}%") 
  }
end
Fill in:
  • Customer name (e.g., “ABC Property Management”)
  • Contact information
  • Optional thumbnail image
2

Add a property

From the customer page, create a new property:
# Property model:
class Property < ApplicationRecord
  belongs_to :customer
  has_many :inspections
end
Navigate to /properties/new and enter:
  • Property name (e.g., “Oak Street Apartments”)
  • Full address
  • Select the customer you just created
Properties can have multiple inspections over time. The system tracks all inspection history per property.

Step 3: Upload a form template

Form templates define the structure of your inspection forms. Upload your first PDF template.
1

Prepare your PDF form

Ensure your PDF has fillable form fields. The system supports:
  • Text fields
  • Checkboxes and radio buttons
  • Signature fields
  • Dropdown lists
PDFs must be AcroForm-based with fillable fields. Scanned PDFs without form fields won’t work.
2

Upload the template

Navigate to /form_templates/new
# FormTemplate model:
class FormTemplate < ApplicationRecord
  has_one_attached :original_file
  has_many :inspections
  has_and_belongs_to_many :interval_categories
end
Fill in:
  • Template name (e.g., “Wet Pipe Sprinkler - Annual”)
  • Upload your PDF file
  • Select system category (e.g., “Wet Pipe”)
  • Choose interval categories (e.g., “Annual”, “Semi-Annual”)
Click “Create” - the system will queue a background job to parse the PDF.
3

Verify parsing results

After upload, the ParseFormTemplateJob extracts all form fields:
# Background job processing:
ParseFormTemplateJob.perform_later(@form_template.id)

# Uses PdfFormsParserService:
parser = PdfFormsParserService.new(file_path)
form_structure = parser.parse
# Returns: [
#   { name: "Location", type: "Text", label_name: "Location" },
#   { name: "Inspector_Signature", type: "Signature_Field" }
# ]
View the template details page to see extracted fields. You can reorder and customize field labels using the form builder at /form_templates/:id/form_builder.

Step 4: Create an inspection

Now you’re ready to schedule an inspection.
1

Start a new inspection

Navigate to /inspections/new or click “New Inspection” from a property page.
# Inspection model:
class Inspection < ApplicationRecord
  belongs_to :property
  belongs_to :form_template
  belongs_to :user  # Assigned technician
  has_many :form_fills
  
  delegate :customer, to: :property
end
2

Configure inspection details

Fill in the inspection form:
  • Customer: Select from dropdown
  • Property: Choose the property (filtered by customer)
  • Date: Inspection date
  • System Category: Type of system (e.g., “Wet Pipe”)
  • Interval Category: Inspection frequency (e.g., “Annual”)
  • Technician: Assign to yourself or another technician
  • Job Number: Optional reference number
The system automatically selects the correct form template based on system category and interval.
3

Automatic form generation

When you create the inspection, the system automatically generates four form fills:
# From InspectionsController#create:
ActiveRecord::Base.transaction do
  @inspection.save!
  
  # 1. Main inspection form
  FormFill.create!(
    name: "#{property.property_name} - #{system_category} - #{interval_category}",
    form_template: main_form_template,
    inspection: @inspection,
    form_structure: main_form_template.form_structure
  )
  
  # 2. Deficiencies form
  FormFill.create!(
    name: "#{property.property_name} - Deficiencies",
    form_template: deficiencies_template,
    inspection: @inspection
  )
  
  # 3. Additional Risers form
  # 4. Corrected Deficiencies form
  
  # Auto-fill header information
  HeaderAutoFillerService.new(@inspection).call
end

Step 5: Fill out the inspection form

Conduct the inspection and fill in the form data.
1

Access the form fill

From the inspection detail page (/inspections/:id), click on the main form fill.You’ll see the form builder interface with all parsed fields organized by section.
2

Enter inspection data

Fill in the form fields:
  • Text fields: Enter observations and measurements
  • Checkboxes: Mark Pass/Fail/N/A for each item
  • Date fields: Auto-populated with inspection date
Data is stored separately from structure:
# FormFill data storage:
form_fill.set_field_value("Sprinkler_Count", "150")
form_fill.set_field_value("System_Status", "Pass")

# Stored in JSONB data column:
# data: {
#   "Sprinkler_Count": "150",
#   "System_Status": "Pass"
# }
3

Attach photos

Add photos to document inspection findings:
# Photo attachment:
result = form_fill.attach_photo_for_field(
  "Control_Valve_Photo",
  uploaded_file
)
# Returns: { 
#   success: true, 
#   attachment_id: "inspection_42_control_valve_photo_a1b2c3d4" 
# }
Each field can have multiple photos. They’re automatically organized by section in the final PDF.
4

Capture signatures

Add technician signature to the form:
# Signature attachment:
form_fill.attach_signature_for_field(
  "Technician_Signature",  # Field name
  signature_image_file     # PNG or JPEG
)
The system differentiates between:
  • Signature_Field - Technician signatures
  • Signature_Annex - Client signatures for final report

Step 6: Record deficiencies (if any)

If you find issues during inspection, document them in the deficiencies form.
1

Access the deficiencies form

From the inspection page, click on the “Deficiencies” form fill.
2

Document each deficiency

For each deficiency:
  • Item: Description of the issue
  • Riser: Location identifier
  • Deficiency/Correction: Select D (Deficiency) or C (Correction)
  • Comment: Detailed notes
# Deficiencies are stored in collections:
deficiency_data = {
  "Item": "Sprinkler head damaged",
  "Riser": "Riser 3, Floor 2",
  "C": "",
  "D": "Yes",
  "comment_value": "Head #23 showing corrosion"
}
3

Automatic deficiency transfer

When you mark the inspection as complete, deficiencies automatically transfer to the correction form:
# From Inspection model:
after_update :trigger_deficiency_transfer, 
  if: -> { saved_change_to_status? && status == 'completed' }

def trigger_deficiency_transfer
  TransferDeficienciesJob.perform_later(id)
end

Step 7: Complete and generate PDF

Finalize the inspection and generate the signed PDF report.
1

Mark inspection complete

Update the inspection status:
# PATCH /inspections/:id/update_status
@inspection.update(status: "completed")
Navigate to /inspections/:id/update_status and change status to “Completed”.
2

Generate the PDF report

The system generates a comprehensive PDF including:
  1. Main form - All inspection data filled in
  2. Deficiencies - Listed deficiencies (if any)
  3. Photo pages - All attached photos organized by section
  4. Signature annex - Client signature page with company branding
# PDF generation uses multiple services:

# 1. Fill the PDF form
parser = PdfFormsParserService.new(template_path)
parser.fill_form(output_path, field_data)

# 2. Merge all sections
merged_pdf = PdfMergingService.new(main_pdf, deficiencies_pdf).merge

# 3. Add photo pages
photos_with_context = form_fill.get_photos_with_context
PdfMergingService.add_images_to_pdf(
  merged_pdf, 
  photos_with_context,
  title: "Inspection Photos"
)

# 4. Add signature annex
PdfMergingService.add_signature_annexes(
  merged_pdf,
  signature_images,
  inspection
)
3

Download or email the report

From the form fill page:
  • Click “Download PDF” to save locally
  • Click “Send Email” to deliver to the customer
The PDF is attached to the form fill via Active Storage:
form_fill.filled_pdf.attach(
  io: File.open(pdf_path),
  filename: "inspection_#{inspection.id}_report.pdf",
  content_type: "application/pdf"
)

Summary statistics

View inspection counts and status:
# Form fill statistics:
counts = form_fill.calculate_form_counts
# Returns: { pass: 45, fail: 2, na: 8 }
The inspection detail page shows:
  • Total Pass/Fail/N/A counts
  • Inspection completion status
  • Attached photos and signatures
  • Linked deficiencies

Next steps

Managing inspections

Schedule and view inspections by month

Generating reports

Learn about PDF generation and email delivery

Offline mode

Learn about mobile offline capabilities

API integration

Integrate with external systems

Troubleshooting

If form template parsing fails:
  • Verify the PDF has fillable AcroForm fields
  • Check that the file is not corrupted
  • Try re-uploading the template
  • Review logs for PdfFormsParserService errors
If signatures don’t show in the final PDF:
  • Verify the signature field type is Signature_Field or Signature_Annex
  • Check that the image file is PNG or JPEG format
  • Ensure the signature was successfully attached (check photos attachments)
  • Review PdfSignatureService logs
If photos appear in wrong sections:
  • Verify the field has a section_name in the form structure
  • Check that photo attachment IDs match the expected pattern
  • Run form_fill.sync_photos_with_structure! to fix mismatches
If deficiencies don’t auto-transfer on completion:
  • Verify the inspection status changed to “completed”
  • Check that TransferDeficienciesJob ran successfully
  • Ensure the “Corrected Deficiencies” template exists
  • Review background job logs
All code examples are from the actual Rails 8 source code at ~/workspace/source/. For more details, reference the controller, model, and service files directly.

Build docs developers (and LLMs) love