# ShiftOps Developer Guide


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

Complete guide for developers working on the ShiftOps tool, covering setup, common tasks, debugging, and optimization.

## Table of Contents

- [Setup](#setup)
- [Development Workflow](#development-workflow)
- [Common Tasks](#common-tasks)
- [Debugging](#debugging)
- [Performance Optimization](#performance-optimization)
- [Testing](#testing)
- [Deployment](#deployment)

## Setup

### Prerequisites

- PHP 7.4+ (8.0+ recommended)
- PHP extensions: `json`, `curl`, `mbstring` (optional, has fallback)
- Web server (Apache/Nginx) or PHP built-in server
- Google Places API key
- HubSpot API token (for customer sync)

### Local Development Setup

1. **Clone Repository**

```bash
git clone <repository-url>
cd landingpage
```

2. **Start PHP Server**

```bash
php -S localhost:8003 -t .
```

3. **Verify Setup**

- Open `http://localhost:8003/v2/pages/shiftops.php`
- Test API: `http://localhost:8003/v2/api/shiftops.php`
- Check health: `http://localhost:8003/v2/api/shiftops-health-check.php`

### File Structure

```
v2/
├── api/
│   ├── shiftops.php                    # Main API (6084 lines)
│   ├── shiftops-customer-matcher.php   # Customer matching (918 lines)
│   ├── shiftops-cost-calculator.php    # Cost calculations (922 lines)
│   ├── shiftops-competitive-analyzer.php  # Competitive analysis (910 lines)
│   ├── shiftops-recommendations-engine.php  # Recommendations (947 lines)
│   ├── shiftops-user-explanations.php  # Score explanations (126 lines)
│   ├── shiftops-hubspot.php            # HubSpot lead submission (325 lines)
│   ├── shiftops-nps.php                # NPS survey submission (238 lines)
│   ├── shiftops-hubspot-customers.php  # Customer sync (547 lines)
│   ├── shiftops-health-check.php        # Diagnostic endpoint (123 lines)
│   └── shiftops-test-matching.php       # Test matching endpoint (172 lines)
├── pages/
│   ├── shiftops.php                     # Entry page (4416 lines)
│   └── shiftops-report.php              # Report page (18310 lines)
├── data/
│   ├── industry_benchmarks.php         # Industry benchmarks
│   └── ordio-customers.json             # Customer list (3018+ customers)
├── config/
│   └── shiftops-customers.php           # Configuration constants
├── cache/
│   └── systems/shiftops/                        # Cache directory
└── js/
    └── shiftops-pdf-generator.js        # PDF export
```

### Configuration

**Environment Variables (Optional):**

- `HUBSPOT_API_TOKEN`: HubSpot API token
- `HUBSPOT_PORTAL_ID`: HubSpot portal ID (default: 145133546)
- `GOOGLE_PLACES_API_KEY`: Google Places API key

**Config File:** `v2/config/shiftops-customers.php`

Contains constants for:

- Customer matching thresholds
- Score boost percentages
- Maximum scores per pillar
- HubSpot configuration

**Note:** Config file is optional - code has fallback constants.

## Development Workflow

### 1. Understanding the Codebase

**Start Here:**

1. Read `docs/systems/shiftops/SHIFTOPS_ARCHITECTURE.md` - System overview
2. Read `docs/systems/shiftops/SHIFTOPS_DATA_STRUCTURES.md` - Data formats
3. Read `docs/systems/shiftops/SHIFTOPS_COMPONENTS.md` - Class documentation
4. Review `.cursor/rules/shiftops-backend.mdc` - Backend patterns
5. Review `.cursor/rules/shiftops-frontend.mdc` - Frontend patterns

### 2. Making Changes

**Before Making Changes:**

- [ ] Review existing code for similar functionality
- [ ] Check related files for dependencies
- [ ] Review documentation for patterns
- [ ] Understand data flow

**During Development:**

- [ ] Follow existing code patterns
- [ ] Add error handling (try-catch)
- [ ] Add logging with request IDs
- [ ] Test with both essential and enhanced modes
- [ ] Test customer matching (if relevant)
- [ ] Test error scenarios

**After Changes:**

- [ ] Test locally (`http://localhost:8003`)
- [ ] Check error logs (`logs/shiftops-debug.log`)
- [ ] Verify cache behavior
- [ ] Test customer matching (if changed)
- [ ] Update documentation if needed

### 3. Code Patterns

**Error Handling:**

```php
try {
    // Operation
} catch (Exception $e) {
    error_log("[{$this->requestId}] Error: " . $e->getMessage());
    error_log("[{$this->requestId}] Stack trace: " . $e->getTraceAsString());
    // Return fallback or null
    return null;
}
```

**Request ID Logging:**

```php
$requestId = uniqid('shiftops_', true);
error_log("[{$requestId}] Operation started");
error_log("[{$requestId}] Operation completed");
```

**Customer Matching:**

```php
if ($this->customerMatcher && method_exists($this->customerMatcher, 'isOrdioCustomer')) {
    $customerMatch = $this->customerMatcher->isOrdioCustomer($businessData);
    if ($customerMatch) {
        $isOrdioCustomer = true;
        $confidence = $customerMatch['confidence'];
    }
}
```

**Cache Check:**

```php
$cacheKey = $this->generateCacheKey($businessData);
$cachedResult = $this->getCachedAnalysis($cacheKey);
if ($cachedResult) {
    return $cachedResult;
}
// Generate new analysis
$this->cacheAnalysis($cacheKey, $analysis);
```

## Common Tasks

### Adding a New Scoring Factor

1. **Identify Pillar**

Determine which pillar the factor affects (scheduling, absence, time tracking, compliance, payroll).

2. **Add to Raw Score Calculation**

Edit the appropriate `calculate*ScoreRaw()` method in `shiftops.php`:

```php
private function calculateSchedulingScoreRaw($businessData, $isOrdioCustomer = false) {
    $score = $baseScore;

    // NEW FACTOR: Add your scoring logic here
    if ($someCondition) {
        $score += 2; // Points for this factor
    }

    // Apply customer boost if applicable
    if ($isOrdioCustomer) {
        $score += CUSTOMER_BASELINE_BONUS;
        $score = $this->applyCustomerBoostRaw($score, BOOST_SCHEDULING_EFFICIENCY, MAX_SCORE_SCHEDULING, MIN_SCORE_SCHEDULING_CUSTOMER);
    }

    return (float)$score;
}
```

3. **Test**

- Test with non-customer business
- Test with customer business
- Verify score ranges (0-20)
- Verify total score calculation

4. **Update Documentation**

- Update `SHIFTOPS_SCORING_SYSTEM.md` with new factor details
- Update `SHIFTOPS_SCORING_ai/QUICK_REFERENCE.md` if needed
- Update `SHIFTOPS_ARCHITECTURE.md` scoring section
- Update `SHIFTOPS_COMPONENTS.md` method documentation

**For complete scoring documentation, see [Scoring System Documentation](SHIFTOPS_SCORING_SYSTEM.md)**

### Adding a New Recommendation

1. **Identify Category**

Determine category: `quick_wins`, `high_value`, or `strategic`.

2. **Add to Recommendations Engine**

Edit `shiftops-recommendations-engine.php`:

```php
private function generateSchedulingRecommendations($businessData, $scores, $businessName, $industry) {
    $recommendations = [];

    // NEW RECOMMENDATION
    if ($someCondition) {
        $recommendations[] = [
            'pillar' => 'scheduling_efficiency',
            'category' => 'quick_wins',
            'priority' => 10,
            'title' => 'Your Recommendation Title',
            'description' => 'Detailed description...',
            'action' => 'Action item for user',
            'impact' => 'high',
            'effort' => 'low',
            'timeline' => '2-4 Wochen',
            'ordio_feature' => 'Schichtplanung',
            'success_metrics' => ['Metric 1', 'Metric 2']
        ];
    }

    return $recommendations;
}
```

3. **Test**

- Verify recommendation appears in correct category
- Test with different business types
- Verify priority sorting

### Modifying Customer Matching

**File:** `v2/api/shiftops-customer-matcher.php`

**Common Modifications:**

1. **Adjust Matching Thresholds**

Edit constants in `v2/config/shiftops-customers.php`:

- `FUZZY_MATCH_THRESHOLD`: Minimum similarity (default: 85)
- `DOMAIN_MATCH_CONFIDENCE`: Domain match confidence (default: 95)

2. **Add New Matching Strategy**

Add to `matchBusinessToCustomer()` method:

```php
// New strategy: Check for common variations
$variations = $this->generateNameVariations($businessName);
foreach ($variations as $variation) {
    // Check against customers
}
```

3. **Test**

Use test endpoint:

```bash
curl "http://localhost:8003/v2/api/shiftops-test-matching.php?name=Test+Business&website=https://example.com"
```

### Updating Customer List

**Manual Update:**

1. Edit `v2/data/ordio-customers.json`
2. Ensure valid JSON structure:

```json
{
  "customers": [
    {
      "company_name": "Business Name",
      "domain": "example.com",
      "normalized_name": "business name",
      "address": "Optional address"
    }
  ]
}
```

**Sync from HubSpot:**

```bash
# Run sync script (if configured)
php v2/api/shiftops-hubspot-customers.php
```

**Verify:**

```bash
curl http://localhost:8003/v2/api/shiftops-health-check.php
```

## Debugging

### Enable Debug Logging

**PHP Error Logging:**

Already enabled in `shiftops.php`:

```php
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/../logs/shiftops-debug.log');
error_reporting(E_ALL);
```

**View Logs:**

```bash
# Real-time log viewing
tail -f logs/shiftops-debug.log

# Search for specific request
grep "shiftops_6914e366c1f3b3" logs/shiftops-debug.log

# View last 100 lines
tail -n 100 logs/shiftops-debug.log
```

### Common Debugging Scenarios

**1. Customer Matching Not Working**

```bash
# Check health
curl http://localhost:8003/v2/api/shiftops-health-check.php

# Test specific business
curl "http://localhost:8003/v2/api/shiftops-test-matching.php?name=La+Fonda&website=https://lafonda.koeln"

# Check logs
grep "isOrdioCustomer" logs/shiftops-debug.log | tail -20
```

**2. Score Calculation Issues**

```bash
# Check score calculation logs
grep "calculateShiftOpsScore" logs/shiftops-debug.log | tail -20

# Check pillar scores
grep "pillar_scores" logs/shiftops-debug.log | tail -20

# Verify score sum matches total
grep "Sum.*Total" logs/shiftops-debug.log | tail -20
```

**3. API Errors**

```bash
# Check for errors
grep "ERROR\|FATAL\|WARNING" logs/shiftops-debug.log | tail -50

# Check specific request
grep "request_id.*shiftops_" logs/shiftops-debug.log | tail -50
```

**4. Cache Issues**

```bash
# Check cache directory
ls -la v2/cache/systems/shiftops/

# Check cache file age
find v2/cache/systems/shiftops/ -name "*.json" -mtime +1

# Clear cache
rm -rf v2/cache/systems/shiftops/*
```

### Browser Debugging

**Frontend Issues:**

1. Open browser DevTools (F12)
2. Check Console tab for JavaScript errors
3. Check Network tab for API calls
4. Check Application tab → Local Storage for data

**Common Frontend Debugging:**

```javascript
// Check localStorage data
console.log(JSON.parse(localStorage.getItem('shiftops_report_data')));

// Check API response
fetch('/v2/api/shiftops.php', {...})
  .then(r => r.json())
  .then(data => console.log('API Response:', data))
  .catch(err => console.error('API Error:', err));
```

## Performance Optimization

### Cache Optimization

**Current Strategy:**

- 1-hour TTL
- File-based caching
- Cache key includes: place_id, name, rating, user_ratings_total, types, mode

**Optimization Tips:**

1. **Increase Cache TTL** (if data doesn't change frequently)

   - Edit cache TTL in `shiftops.php`
   - Current: 3600 seconds (1 hour)

2. **Cache Warming**

   - Pre-generate cache for popular businesses
   - Run cron job to refresh cache

3. **Cache Cleanup**
   - Remove old cache files (> 24 hours)
   - Implement cache size limits

### API Optimization

**Current Performance:**

- Essential mode: < 2 seconds
- Enhanced mode: 3-5 seconds
- Cache hits: < 100ms

**Optimization Strategies:**

1. **Parallel API Calls** (Enhanced Mode)

   - Fetch weather and holidays in parallel
   - Use `curl_multi_exec()` for concurrent requests

2. **Lazy Loading**

   - Only load customer list when needed (already implemented)
   - Load competitive analysis only in enhanced mode

3. **Database Caching** (Future)
   - Replace file cache with Redis/Memcached
   - Faster lookups, distributed caching

### Memory Optimization

**Current Memory Usage:**

- Customer list: ~1.35 MB (3018 customers)
- Analysis: ~50-100 KB per request
- Cache files: ~50-200 KB each

**Optimization Tips:**

1. **Stream JSON Parsing** (for large customer lists)

   - Use `json_decode()` with streaming
   - Process customers in batches

2. **Memory Limits**
   - Monitor with `memory_get_usage()`
   - Set appropriate `memory_limit` in PHP

## Testing

### Unit Testing

**Test Scoring Algorithm:**

```php
// Test score calculation
$analyzer = new ShiftOpsAnalyzer();
$businessData = [
    'place_id' => 'test',
    'name' => 'Test Restaurant',
    'rating' => 4.5,
    'user_ratings_total' => 100
];
$score = $analyzer->calculateShiftOpsScore($businessData, false);
assert($score['total_score'] >= 0 && $score['total_score'] <= 100);
assert(array_sum($score['pillar_scores']) == $score['total_score']);
```

**Test Customer Matching:**

```php
$matcher = new ShiftOpsCustomerMatcher();
$businessData = [
    'name' => 'La Fonda',
    'website' => 'https://lafonda.koeln'
];
$match = $matcher->isOrdioCustomer($businessData);
assert($match !== null);
assert($match['confidence'] >= 95);
```

### Integration Testing

**Test Complete Flow:**

1. **Essential Mode:**

```bash
curl -X POST http://localhost:8003/v2/api/shiftops.php \
  -H "Content-Type: application/json" \
  -d '{"mode":"essential","place_id":"ChIJN1t_tDeuEmsRUsoyG83frY4","name":"Test Restaurant"}'
```

2. **Enhanced Mode:**

```bash
curl -X POST http://localhost:8003/v2/api/shiftops.php \
  -H "Content-Type: application/json" \
  -d '{"mode":"enhanced","place_id":"ChIJN1t_tDeuEmsRUsoyG83frY4","name":"Test Restaurant"}'
```

3. **HubSpot Submission:**

```bash
curl -X POST http://localhost:8003/v2/api/shiftops-hubspot.php \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","shiftops_index":65}'
```

### End-to-End Testing

**Manual Testing Checklist:**

- [ ] Search flow works (Google Places autocomplete)
- [ ] Essential mode returns data (< 2s)
- [ ] Enhanced mode returns full data (3-5s)
- [ ] Report page displays correctly
- [ ] Customer matching works (La Fonda = 95 score)
- [ ] Score calculations are correct
- [ ] localStorage stores/retrieves data correctly
- [ ] PDF export works
- [ ] Mobile layout works
- [ ] Desktop layout works
- [ ] Error handling works (invalid input, API failures)
- [ ] HubSpot submission works

## Deployment

### Pre-Deployment Checklist

- [ ] Test all endpoints locally
- [ ] Verify customer matching works
- [ ] Check error logs for warnings
- [ ] Verify cache directory exists and is writable
- [ ] Verify log directory exists and is writable
- [ ] Check file permissions (cache: 755, logs: 755)
- [ ] Verify customer list file exists (`v2/data/ordio-customers.json`)
- [ ] Test production API endpoints
- [ ] Verify HubSpot integration works
- [ ] Check performance (response times)

### Deployment Steps

1. **Commit Changes**

```bash
git add .
git commit -m "Description of changes"
git push origin master
```

2. **Verify on Production**

```bash
# Test health check
curl https://www.ordio.com/v2/api/shiftops-health-check.php

# Test API
curl -X POST https://www.ordio.com/v2/api/shiftops.php \
  -H "Content-Type: application/json" \
  -d '{"mode":"essential","place_id":"ChIJ...","name":"Test"}'
```

3. **Monitor Logs**

```bash
# Check for errors
tail -f /path/to/logs/shiftops-debug.log

# Check HubSpot submissions
tail -f /path/to/logs/shiftops-hubspot.log
```

### Post-Deployment

- [ ] Monitor error logs for 24 hours
- [ ] Check API response times
- [ ] Verify customer matching accuracy
- [ ] Test end-to-end flow on production
- [ ] Check HubSpot lead submissions

## Best Practices

### Code Quality

1. **Always Use Request IDs**

```php
$requestId = uniqid('shiftops_', true);
error_log("[{$requestId}] Operation started");
```

2. **Error Handling**

```php
try {
    // Operation
} catch (Exception $e) {
    error_log("[{$requestId}] Error: " . $e->getMessage());
    // Return fallback, never crash
    return null;
}
```

3. **Validate Input**

```php
if (empty($input['place_id']) || empty($input['name'])) {
    http_response_code(400);
    echo json_encode(['success' => false, 'error' => 'Missing required fields']);
    exit;
}
```

4. **Document Complex Logic**

```php
/**
 * Calculates scheduling efficiency score
 *
 * Factors:
 * - Digital maturity (0-5 points)
 * - Operational complexity (0-7 points)
 * - Team scale indicators (0-3 points)
 *
 * Customer boost: +3 baseline + 35% boost (max 18)
 */
private function calculateSchedulingScoreRaw($businessData, $isOrdioCustomer = false) {
    // Implementation
}
```

### Performance

1. **Use Caching**

Always check cache before expensive operations.

2. **Lazy Loading**

Load data only when needed (customer list, competitive analysis).

3. **Early Exit**

Exit early if conditions aren't met (exact match found, invalid input).

4. **Monitor Performance**

Log execution times for slow operations:

```php
$startTime = microtime(true);
// Operation
$duration = (microtime(true) - $startTime) * 1000;
error_log("[{$requestId}] Operation took {$duration}ms");
```

## Troubleshooting

See [SHIFTOPS_TROUBLESHOOTING.md](SHIFTOPS_TROUBLESHOOTING.md) for detailed troubleshooting guide.

**Quick Fixes:**

1. **Customer matching fails** → Check `shiftops-health-check.php`
2. **Scores incorrect** → Check logs for calculation errors
3. **API slow** → Check cache, verify external APIs
4. **Cache not working** → Check directory permissions (755)
5. **mbstring error** → Already fixed with `safeStrToLower()` fallback

## Resources

- **Architecture:** `SHIFTOPS_ARCHITECTURE.md`
- **API Reference:** `SHIFTOPS_API_DOCUMENTATION.md`
- **Data Structures:** `SHIFTOPS_DATA_STRUCTURES.md`
- **Components:** `SHIFTOPS_COMPONENTS.md`
- **Troubleshooting:** `SHIFTOPS_TROUBLESHOOTING.md`
- **Quick Reference:** `SHIFTOPS_ai/QUICK_REFERENCE.md`
- **Cursor Rules:** `.cursor/rules/shiftops-backend.mdc`, `.cursor/rules/shiftops-frontend.mdc`

## Getting Help

1. Check documentation first
2. Review error logs (`logs/shiftops-debug.log`)
3. Use health check endpoint
4. Use test matching endpoint for debugging
5. Review recent changes in git history
