# ShiftOps Team Estimation - Data Flow Documentation


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

## Data Structure Locations

### Primary Storage Locations

1. **`analysis_data.online_presence.team_size_estimate`** (integer)

   - Set by: `ShiftOpsAnalyzer::analyzeOnlinePresence()` → `estimateTeamSize()`
   - Used in: Loading screen display, fallback calculations

2. **`analysis_data.cost_savings.team_size_estimate`** (integer or array)
   - Set by: `ShiftOpsCostCalculator::calculateCostSavings()` → `estimateTeamSize()`
   - Structure: Integer (legacy) or `team_size` from array (new)
   - Used in: Cost savings calculations, ROI analysis, report displays

## Data Flow Paths

### Path 1: Analyzer → Online Presence

```
ShiftOpsAnalyzer::analyzeBusiness()
  ↓
analyzeOnlinePresence($businessData)
  ↓
estimateTeamSize($businessData)  [v2/api/shiftops.php:2018]
  ↓ Returns: int
  ↓
Stored in: analysis_data.online_presence.team_size_estimate
```

**Usage Points:**

- `v2/pages/shiftops.php:2822` - Loading screen display
- `v2/pages/shiftops.php:1906` - Fallback data structure
- `v2/api/shiftops.php:1056` - Fallback cost savings calculation
- `v2/api/shiftops.php:328` - Fallback cost savings calculation

### Path 2: Cost Calculator → Cost Savings

```
ShiftOpsAnalyzer::analyzeBusiness()
  ↓
calculateCostSavings($businessData, $shiftopsScores)
  ↓
ShiftOpsCostCalculator::calculateCostSavings()
  ↓
estimateTeamSize($businessData)  [v2/api/shiftops-cost-calculator.php:91]
  ↓ Returns: array['team_size' => int, ...]
  ↓
Extract: $teamSize = $teamSizeData['team_size']
  ↓
Stored in: analysis_data.cost_savings.team_size_estimate
```

**Usage Points:**

- `v2/pages/shiftops-report.php:5829` - Report cost savings display
- `v2/pages/shiftops-report.php:8059` - Team size value element
- `v2/pages/shiftops-report.php:13667` - Team size insight section
- `v2/pages/shiftops-report.php:15676` - Detailed analysis table
- `v2/pages/shiftops-report.php:15941` - PDF export
- `v2/api/shiftops-recommendations-engine.php:753` - Recommendations calculations
- `v2/js/shiftops-pdf-generator.js:241` - PDF generation

### Path 3: JavaScript Fallback

```
shiftops-report.php (client-side)
  ↓
estimateTeamSizeFromReviews(reviewCount, businessTypes)  [line 6867]
  ↓ Returns: object {estimated_team_size, confidence, ...}
  ↓
Used when: API data unavailable or incomplete
```

**Usage Points:**

- `v2/pages/shiftops-report.php:6770` - Fallback analysis data
- `v2/pages/shiftops-report.php:6977` - Operational insights fallback
- `v2/pages/shiftops-report.php:9444` - Element update fallback

## Display Locations

### Loading Screen (`shiftops.php`)

**Line 2822:**

```javascript
const teamSize =
  onlinePresence.team_size_estimate ||
  Math.max(10, Math.floor(reviewCount / 50));
```

**Line 2857:**

```javascript
~${teamSize} Mitarbeiter
```

**Line 3218:**

```javascript
• Erwartete Zeitersparnis: ${Math.round((costSavings.team_size_estimate || 20) * 2.5)}h/Woche
```

**Fallback (Line 1906):**

```javascript
team_size_estimate: Math.max(
  10,
  Math.floor((place?.user_ratings_total || 0) / 50)
);
```

### Report Page (`shiftops-report.php`)

**Primary Display (Line 8059):**

```javascript
formatValue(data.analysis_data?.cost_savings?.team_size_estimate, "count");
```

**Team Size Insight Section (Line 13667-13711):**

```javascript
const teamSizeInsight = document.getElementById("team-size-insight");
const teamSizeValue = document.getElementById("team-size-value");
const teamSizeDetail = document.getElementById("team-size-detail");
```

**Validation Check (Line 7563):**

```javascript
hasTeamSizeEstimate: !!(
  data.analysis_data?.team_size_estimate?.estimated_team_size > 0
);
```

**Note:** This validation expects `team_size_estimate` to be an object with `estimated_team_size` property, but Cost Calculator returns integer directly in `cost_savings.team_size_estimate`.

**Detailed Table (Line 15676):**

```javascript
${formatNumber(analysis.cost_savings?.team_size_estimate)} Mitarbeiter
```

**PDF Export (Line 15941):**

```javascript
const teamSize =
  getNestedValue(analysis, "cost_savings.team_size_estimate") || null;
```

**Operational Insights (Line 14874):**

```javascript
const teamSize = bizInfo.user_ratings_total
  ? Math.max(5, Math.min(50, Math.round(bizInfo.user_ratings_total / 20)))
  : 10;
```

**Note:** This uses a different formula: `reviews / 20` with min 5, max 50.

## Fallback Scenarios

### Scenario 1: Cost Calculator Unavailable

**Location:** `v2/api/shiftops.php:4615-4624`

```php
// Fallback cost savings calculation
$teamSize = $businessData['user_ratings_total'] ?? 0;
$teamSize = $teamSize > 0 ? max(10, min(100, round($teamSize / 50))) : 10;
```

**Formula:** `reviews / 50` with min 10, max 100

### Scenario 2: Cost Calculator Error

**Location:** `v2/api/shiftops.php:1056-1063`

```php
$teamSize = $onlinePresence['team_size_estimate'] ?? 10;
```

**Uses:** Online presence estimate or default 10

### Scenario 3: Client-Side Fallback

**Location:** `v2/pages/shiftops-report.php:6867`

```javascript
function estimateTeamSizeFromReviews(reviewCount, businessTypes = []) {
  // Simple sqrt formula
  baseEstimate = Math.sqrt(reviewCount) * multiplier;
  return Math.max(1, Math.round(baseEstimate));
}
```

### Scenario 4: Loading Screen Fallback

**Location:** `v2/pages/shiftops.php:2822`

```javascript
const teamSize =
  onlinePresence.team_size_estimate ||
  Math.max(10, Math.floor(reviewCount / 50));
```

**Formula:** `reviews / 50` with min 10

## Data Structure Differences

### Analyzer Return

```php
// Integer only
team_size_estimate: 12
```

### Cost Calculator Return

```php
// Array with metadata
team_size_estimate: 12  // Extracted from array['team_size']
// Full structure available in:
team_size_confidence: 'high'
team_size_factors: ['volume' => 1.2, 'hours' => 1.1, ...]
```

### JavaScript Fallback Return

```javascript
// Object with metadata
{
    estimated_team_size: 12,
    confidence: 'high',
    primary_type: 'restaurant',
    multiplier: 0.8
}
```

## Access Patterns

### Pattern 1: Direct Integer Access

```php
$teamSize = $analysis['cost_savings']['team_size_estimate'];
```

### Pattern 2: Nested Object Access (Incorrect)

```javascript
// This expects object but gets integer
data.analysis_data?.team_size_estimate?.estimated_team_size;
```

### Pattern 3: Safe Access with Fallback

```javascript
costSavings.team_size_estimate || 10;
```

### Pattern 4: Format Value Function

```javascript
formatValue(data.analysis_data?.cost_savings?.team_size_estimate, "count");
```

## Inconsistencies in Data Access

1. **Validation expects object, gets integer:**

   - Line 7563: `data.analysis_data?.team_size_estimate?.estimated_team_size`
   - But Cost Calculator stores integer directly

2. **Different fallback formulas:**

   - Analyzer fallback: `reviews / 50` (min 10, max 100)
   - Loading screen: `reviews / 50` (min 10)
   - Operational insights: `reviews / 20` (min 5, max 50)
   - JavaScript fallback: `sqrt(reviews) * multiplier` (min 1)

3. **Different default values:**

   - Analyzer: 10
   - Cost Calculator: Based on base staffing (3-8)
   - JavaScript: 1
   - Loading screen: 10

4. **Structure mismatch:**
   - Cost Calculator returns array but stores integer
   - Validation expects object structure
   - Some code accesses as integer, some as object

## Default Values Used

| Location             | Default | Context                   |
| -------------------- | ------- | ------------------------- |
| Analyzer fallback    | 10      | When calculation fails    |
| Cost Calculator base | 3-8     | Industry-specific minimum |
| JavaScript fallback  | 1       | Minimum team size         |
| Loading screen       | 10      | When data missing         |
| Report validation    | 0       | When checking existence   |
| Operational insights | 10      | When data missing         |

## Migration Considerations

When updating the estimation logic:

1. **Maintain integer compatibility** - Many places expect integer
2. **Update validation** - Fix object vs integer mismatch
3. **Standardize fallbacks** - Use same formula everywhere
4. **Update access patterns** - Ensure consistent structure
5. **Handle both formats** - Support legacy integer and new array format during transition
