Company Completion Score System
Ruby-based completion score calculation with tiered field weighting for company profile completeness.
Overview
The completion score measures how complete a company's profile is, based on presence of key fields across three importance tiers. Score ranges from 0.00 (empty) to 1.00 (complete).
Architecture
Location: app/models/concerns/completable.rb
Storage: companies_infos.completion_score (decimal)
Triggers: Automatic via model callbacks on Company and Companies::Info
Tiered Field Weighting
Tier 1: Critical Identity (weight: 3)
Essential for company identification and classification:
name- Legal company namelegal_status_id- Legal form (SA, SARL, etc.)naf_id- NAF activity codestart_date- Operations start datedescription_id- Activity description
Tier 2: Important Business Data (weight: 2)
Key operational and legal information:
date_registered- Official registration datecapital_amount- Company capitalmain_activity- Primary activity textrep_type_id- Representative typecommercial_name- Trading namecompany_rep- Representative info
Tier 3: Supplementary Details (weight: 1)
Additional profile enhancements:
currency_code- Capital currencyduration- Company durationend_date- End date (if applicable)tax_year_date- Fiscal year endsubsidiary_id- Branch typeupdated- Last update timestamp
Score Calculation
total_points = (critical_filled * 3) + (important_filled * 2) + (supplementary_filled * 1)
max_points = (5 * 3) + (6 * 2) + (6 * 1) # = 33
score = total_points / max_points
Example: Company with name, NAF, legal_status, capital_amount
- Critical: 3 fields × 3 = 9 points
- Important: 1 field × 2 = 2 points
- Score: 11 / 33 = 0.33 (33%)
Usage
Automatic Updates
Scores update automatically when relevant fields change:
company.update(commercial_name: "New Name")
# → completion score automatically recalculated
Manual Calculation
# Calculate without saving
score = company.calculate_completion_score
# => 0.67
# Update stored score
company.update_completion_score
# => true
# Get tier breakdown
company.completion_breakdown
# => { critical: {...}, important: {...}, supplementary: {...}, overall: {...} }
Rake Tasks
# Recalculate all companies
rake completion_scores:recalculate
# Recalculate since date
rake completion_scores:recalculate_since[2025-01-01]
# View statistics
rake completion_scores:stats
Background Job
Scheduled daily at 2 AM via UpdateCompletionScoresJob:
# Manual trigger
UpdateCompletionScoresJob.perform_later
# Force full update
UpdateCompletionScoresJob.perform_later(force_full_update: true)
Integration Points
Elasticsearch
Score indexed as integer (0-100):
# In ElasticSearch::CompanySearch concern
completion_score: (company.infos&.completion_score || 0) * 100
API Serialization
Exposed in API as percentage:
# CompanySerializer
completion_score: (company.infos&.completion_score || 0.0) * 100
# => Returns 0-100
Admin Filters
Range filtering in admin interface:
# API params
filters: {
completion_score_min: 50, # 50%
completion_score_max: 100 # 100%
}
Performance
- Conditional Triggers: Only recalculates when tracked fields change
- Batch Processing:
find_in_batchesfor large updates - No N+1 Queries: Direct field access, no joins required
- Synchronous: Runs in-process (sub-millisecond per company)
Migration from SQL Function
Previous implementation used companies_completion_score_optimized() SQL function. New Ruby implementation provides:
✅ Maintainable Ruby code vs complex SQL ✅ Tiered importance vs equal weighting ✅ Testable via RSpec ✅ Better performance (no SQL function calls) ✅ Conditional triggers (only when needed)
Testing
bundle exec rspec spec/models/concerns/completable_spec.rb
Tests cover:
- Score calculation logic for each tier
- Automatic hook triggers
- Edge cases (empty, partial, full profiles)
- Breakdown reporting
Troubleshooting
Score not updating
Check if companies_infos record exists:
company.infos.present? # Should be true
Incorrect scores
Verify field values:
company.completion_breakdown
# Shows exactly which fields are counted
Bulk recalculation needed
After schema changes or migrations:
rake completion_scores:recalculate