# ShiftOps Scoring Quick Reference


**Last Updated:** 2025-11-20

Quick lookup guide for ShiftOps scoring system constants, ranges, and common patterns.

## Pillar Score Ranges

| Pillar                | Range | Max (Customer) | Min (Customer) |
| --------------------- | ----- | -------------- | -------------- |
| Scheduling Efficiency | 0-20  | 18             | 14             |
| Absence Stability     | 0-20  | 20             | 15             |
| Time Tracking Hygiene | 0-20  | 18             | 14             |
| Compliance Docs       | 0-20  | 19             | 15             |
| Payroll Readiness     | 0-20  | 18             | 14             |

## Total Score Ranges

| Score Range | Grade |
| ----------- | ----- |
| 90-100      | A+    |
| 85-89       | A     |
| 80-84       | A-    |
| 75-79       | B+    |
| 70-74       | B     |
| 65-69       | B-    |
| 60-64       | C+    |
| 55-59       | C     |
| 50-54       | C-    |
| 45-49       | D+    |
| 40-44       | D     |
| 35-39       | D-    |
| 0-34        | F     |

## Configuration Constants

### Boost Percentages

```php
BOOST_SCHEDULING_EFFICIENCY = 35  // +35%
BOOST_TIME_TRACKING = 35          // +35%
BOOST_COMPLIANCE_DOCS = 30        // +30%
BOOST_ABSENCE_STABILITY = 25      // +25%
BOOST_PAYROLL_READINESS = 30      // +30%
```

### Maximum Scores

```php
MAX_SCORE_SCHEDULING = 18
MAX_SCORE_TIME_TRACKING = 18
MAX_SCORE_COMPLIANCE = 19
MAX_SCORE_ABSENCE = 20
MAX_SCORE_PAYROLL = 18
MAX_TOTAL_SCORE = 95
```

### Minimum Customer Scores

```php
MIN_SCORE_SCHEDULING_CUSTOMER = 14
MIN_SCORE_TIME_TRACKING_CUSTOMER = 14
MIN_SCORE_COMPLIANCE_CUSTOMER = 15
MIN_SCORE_ABSENCE_CUSTOMER = 15
MIN_SCORE_PAYROLL_CUSTOMER = 14
```

### Baseline Bonus

```php
CUSTOMER_BASELINE_BONUS = 3  // Points added before percentage boost
```

## Data Completeness Multipliers

| Complete Fields | Percentage | Multiplier | Customer Multiplier |
| --------------- | ---------- | ---------- | ------------------- |
| 4 fields        | 100%       | 1.0        | 1.0                 |
| 3 fields        | 75%        | 0.90       | 0.95 (min)          |
| 2 fields        | 50%        | 0.75       | 0.95 (min)          |
| 1 field         | 25%        | 0.65       | 0.95 (min)          |
| 0 fields        | 0%         | 0.60       | 0.95 (min)          |

**Fields Checked:** Website, Opening Hours, Reviews, Photos

## Industry Baselines

### Scheduling Efficiency

| Industry    | Baseline |
| ----------- | -------- |
| Healthcare  | 5        |
| Hospitality | 6        |
| Retail      | 7        |
| General     | 6        |

### Absence Stability

| Industry    | Baseline |
| ----------- | -------- |
| Healthcare  | 6        |
| Hospitality | 7        |
| Retail      | 8        |
| General     | 7        |

### Time Tracking Hygiene

| Industry    | Baseline |
| ----------- | -------- |
| Healthcare  | 5        |
| Hospitality | 5        |
| Retail      | 6        |
| General     | 6        |

### Compliance Docs

| Industry    | Baseline |
| ----------- | -------- |
| Healthcare  | 9        |
| Hospitality | 5        |
| Retail      | 5        |
| General     | 6        |

### Payroll Readiness

| Industry    | Baseline |
| ----------- | -------- |
| Healthcare  | 6        |
| Hospitality | 6        |
| Retail      | 7        |
| General     | 7        |

## Common Scoring Patterns

### High Score Business

**Characteristics:**

- Website with online features
- Active Google Business (photos >10, reviews >50)
- High weekly hours (>60)
- Multiple service types
- High rating (>=4.0) and review count (>100)
- Complete data (4 fields)

**Typical Scores:**

- Scheduling: 15-18
- Absence: 14-18
- Time Tracking: 14-18
- Compliance: 15-19
- Payroll: 14-18
- Total: 70-90

### Medium Score Business

**Characteristics:**

- Website exists
- Moderate Google Business presence
- Standard weekly hours (40-60)
- Average rating (3.5-4.0)
- Moderate review count (50-100)
- Partial data (2-3 fields)

**Typical Scores:**

- Scheduling: 10-14
- Absence: 10-14
- Time Tracking: 10-14
- Compliance: 10-15
- Payroll: 10-14
- Total: 50-70

### Low Score Business

**Characteristics:**

- No website or minimal digital presence
- Low Google Business activity
- Limited weekly hours (<40)
- Low rating (<3.5)
- Low review count (<50)
- Incomplete data (0-1 fields)

**Typical Scores:**

- Scheduling: 5-10
- Absence: 5-10
- Time Tracking: 5-10
- Compliance: 5-10
- Payroll: 5-10
- Total: 25-50

## Customer Boost Calculation

**Formula:**

```php
// Step 1: Add baseline bonus
$score += CUSTOMER_BASELINE_BONUS;  // +3

// Step 2: Apply percentage boost
$boost = $score * (BOOST_PERCENT / 100);
$boostedScore = $score + $boost;

// Step 3: Apply minimum floor (if below)
if ($boostedScore < MIN_SCORE) {
    $boostedScore = MIN_SCORE;
}

// Step 4: Apply maximum cap
$boostedScore = min($boostedScore, MAX_SCORE);
```

**Example:**

- Original score: 12.0
- Baseline bonus: +3 = 15.0
- Percentage boost (35%): 15.0 \* 0.35 = 5.25
- Boosted: 15.0 + 5.25 = 20.25
- Capped at max: min(20.25, 18) = **18.0**

## Data Completeness Calculation

**Fields Checked:**

1. Website (exists)
2. Opening hours (exists)
3. Reviews (exists and count >0)
4. Photos (exists and count >0)

**Multiplier Logic:**

```php
$completeFields = count(array_filter([
    !empty($businessData['website']),
    !empty($businessData['opening_hours']),
    !empty($businessData['reviews']) && count($businessData['reviews']) > 0,
    !empty($businessData['photos']) && count($businessData['photos']) > 0
]));

if ($completeFields >= 4) {
    $multiplier = 1.0;
} elseif ($completeFields >= 3) {
    $multiplier = 0.90;
} elseif ($completeFields >= 2) {
    $multiplier = 0.75;
} elseif ($completeFields >= 1) {
    $multiplier = 0.65;
} else {
    $multiplier = 0.60;
}
```

## ROI Calculation

**Formula:**

```php
$annualSavings = $monthlySavings * 12;
$ordioAnnualCost = $ordioMonthlyCost * 12;  // €89/month * 12
$netBenefit = $annualSavings - $ordioAnnualCost;
$roiPercentage = ($ordioAnnualCost > 0) ? ($netBenefit / $ordioAnnualCost) * 100 : 0;
$roiPercentage = min(500, max(-100, $roiPercentage));  // Cap at 500%
```

**Key Points:**

- ROI is calculated using **annual** values (not monthly)
- ROI is capped at **500%** to prevent unrealistic values
- ROI can be negative (up to -100%) if savings don't cover costs

## Restaurant-Specific Scoring Reductions

**Purpose:** Prevent restaurant score inflation due to naturally high review counts and operational hours

**Reductions Applied:**

1. **Digital Maturity Bonus:**

   - Restaurants: +1 point (reduced from +2) for photos >10 AND reviews >50
   - Restaurants: +0.5 points (reduced from +1) for photos >5 OR reviews >25
   - Other types: Original values (+2, +1)

2. **Review Count Impact:**

   - Restaurants: +0.5 points (reduced from +1) for reviews >500
   - Restaurants: +0.5 points (reduced from +1) for reviews >100
   - Other types: Original values (+1, +1)

3. **Operational Complexity:**
   - Restaurants: +4 points (reduced from +6) for weekly hours >=80
   - Restaurants: +2 points (reduced from +4) for weekly hours >=60
   - Restaurants: +1 point (reduced from +2) for weekly hours >=40
   - Other types: Original values (+6, +4, +2)

**Code Location:** `v2/api/shiftops.php` lines 3210-3260

## Pillar Score Adjustment Algorithm

**Purpose:** Ensure pillar scores sum to total score

**Steps:**

1. Calculate ratio: `$ratio = $targetTotal / $rawSum`
2. Apply ratio: `$adjusted = $rawScore * $ratio`
3. Floor all scores
4. Calculate difference: `$difference = $targetTotal - $sumOfAdjusted`
5. If difference >0: Round up pillars with highest fractional parts
6. If difference <0: Round down pillars with lowest fractional parts
7. Final verification: Adjust largest/smallest if needed

## Debugging Tips

### Check Raw Scores

```php
error_log("Raw pillar scores: " . json_encode($rawScores));
```

### Check Data Completeness

```php
error_log("Data completeness: " . json_encode($dataCompleteness));
```

### Check Customer Boost

```php
error_log("Customer boost applied: " . ($isOrdioCustomer ? 'yes' : 'no'));
```

### Verify Pillar Sum

```php
$sum = array_sum($pillarScores);
if ($sum != $totalScore) {
    error_log("WARNING: Sum ({$sum}) != Total ({$totalScore})");
}
```

### Check Grade Assignment

```php
$grade = getGradeFromScore($totalScore);
error_log("Total: {$totalScore}, Grade: {$grade}");
```

## Common Issues

### Pillar Scores Don't Sum to Total

**Cause:** Rounding errors in adjustment algorithm

**Solution:** Check `adjustPillarScoresToMatchTotal()` function, verify final verification step runs

### Customer Score Too Low

**Cause:** Missing baseline bonus or boost not applied

**Solution:** Verify `CUSTOMER_BASELINE_BONUS` is defined and added before percentage boost

### Data Completeness Too Strict

**Cause:** Missing fields incorrectly penalized

**Solution:** Check field existence logic, verify customer adjustment for missing website

### Grade Doesn't Match Score

**Cause:** Grade calculation uses different ranges than expected

**Solution:** Verify `getGradeFromScore()` matches documented ranges

## Quick Code Snippets

### Calculate Score

```php
$shiftopsScore = $analyzer->calculateShiftOpsScore($businessData, $isOrdioCustomer);
```

### Get Grade

```php
$grade = $analyzer->getGradeFromScore($totalScore);
```

### Check Data Completeness

```php
$dataCompleteness = $analyzer->calculateDataCompleteness($businessData);
$multiplier = $dataCompleteness['multiplier'];
```

### Apply Customer Boost

```php
$boostedScore = $analyzer->applyCustomerBoostRaw(
    $score,
    BOOST_SCHEDULING_EFFICIENCY,
    MAX_SCORE_SCHEDULING,
    MIN_SCORE_SCHEDULING_CUSTOMER
);
```

## Related Documentation

- [Scoring System Documentation](SHIFTOPS_SCORING_SYSTEM.md) - Complete scoring documentation
- [ShiftOps Architecture](SHIFTOPS_ARCHITECTURE.md) - System architecture
- [ShiftOps Components](SHIFTOPS_COMPONENTS.md) - Component documentation
