Services Created: - OMGService.php: OMG API integration with detailed error handling - CSVTransformer.php: CSV parsing and transformation logic - EmailService.php: Mailgun email notifications - process-csv.php: Multi-stage CSV processing with progress tracking - upload-to-box.php: Box upload with approval workflow Features: - Comprehensive validation at each stage (upload, parse, campaign, API, transform) - Detailed error reporting with actionable messages - Warning system for non-critical issues - Progress tracking through all stages - Session-based CSV storage for preview before upload - Date transformation (parse + add 1 month per blueprint) - 16x market multiplication per ISO codes - Business unit mapping per Make.com blueprint logic Dependencies Added: - league/csv for CSV parsing - nesbot/carbon for date manipulation Configuration: - Added global_to_local settings (ISO codes, business unit map) - Added omg_api settings (placeholder for API key) - Added email settings (Mailgun placeholders) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
243 lines
8.3 KiB
PHP
243 lines
8.3 KiB
PHP
<?php
|
|
/**
|
|
* Process CSV Upload
|
|
* Handles CSV transformation with detailed progress tracking and error reporting
|
|
*/
|
|
|
|
error_reporting(E_ALL);
|
|
ini_set('display_errors', 1);
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
try {
|
|
// Load dependencies
|
|
require_once __DIR__ . '/vendor/autoload.php';
|
|
require_once __DIR__ . '/AuthMiddleware.php';
|
|
require_once __DIR__ . '/CSVTransformer.php';
|
|
require_once __DIR__ . '/OMGService.php';
|
|
require_once __DIR__ . '/EmailService.php';
|
|
|
|
// Authenticate
|
|
$auth = new AuthMiddleware();
|
|
$user = $auth->requireAuth();
|
|
|
|
// Check if file was uploaded
|
|
if (!isset($_FILES['csvFile'])) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'stage' => 'upload',
|
|
'error' => 'No file uploaded',
|
|
'details' => 'Please select a CSV file to upload'
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
$file = $_FILES['csvFile'];
|
|
$transformer = new CSVTransformer();
|
|
$omgService = new OMGService();
|
|
$emailService = new EmailService();
|
|
|
|
$progress = [];
|
|
|
|
// STAGE 1: Validate Upload
|
|
$progress[] = ['stage' => 'upload', 'status' => 'processing', 'message' => 'Validating file upload...'];
|
|
|
|
$validation = $transformer->validateUpload($file);
|
|
|
|
if (!$validation['valid']) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'stage' => 'upload',
|
|
'error' => 'File validation failed',
|
|
'details' => implode('; ', $validation['errors']),
|
|
'action' => 'Please upload a valid CSV file (max 5MB)',
|
|
'progress' => $progress
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
$filename = $validation['filename'];
|
|
$progress[] = ['stage' => 'upload', 'status' => 'success', 'message' => 'File uploaded: ' . $filename];
|
|
|
|
// STAGE 2: Parse CSV
|
|
$progress[] = ['stage' => 'parse', 'status' => 'processing', 'message' => 'Parsing CSV file...'];
|
|
|
|
$parseResult = $transformer->parseCSV($file['tmp_name']);
|
|
|
|
if (!$parseResult['success']) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'stage' => 'parse',
|
|
'error' => $parseResult['error'],
|
|
'details' => $parseResult['details'] ?? '',
|
|
'action' => $parseResult['action'] ?? 'Check CSV format',
|
|
'progress' => $progress
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
$rowCount = $parseResult['rowCount'];
|
|
$progress[] = ['stage' => 'parse', 'status' => 'success', 'message' => "Parsed $rowCount data rows"];
|
|
|
|
// STAGE 3: Extract Campaign Number
|
|
$progress[] = ['stage' => 'campaign', 'status' => 'processing', 'message' => 'Extracting campaign number...'];
|
|
|
|
$campaignResult = $transformer->extractCampaignNumber($filename);
|
|
|
|
if (!$campaignResult['success']) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'stage' => 'campaign',
|
|
'error' => $campaignResult['error'],
|
|
'details' => $campaignResult['details'] ?? '',
|
|
'action' => $campaignResult['action'] ?? 'Fix filename format',
|
|
'progress' => $progress
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
$campaignNumber = $campaignResult['campaignNumber'];
|
|
$message = 'Campaign number: ' . $campaignNumber;
|
|
if (isset($campaignResult['warning'])) {
|
|
$message .= ' (warning: ' . $campaignResult['warning'] . ')';
|
|
}
|
|
$progress[] = ['stage' => 'campaign', 'status' => 'success', 'message' => $message];
|
|
|
|
// STAGE 4: Call OMG API
|
|
$progress[] = ['stage' => 'omg_api', 'status' => 'processing', 'message' => 'Looking up campaign in OMG...'];
|
|
|
|
$projectResult = $omgService->getProject($campaignNumber);
|
|
|
|
if (!$projectResult['success']) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'stage' => 'omg_api',
|
|
'error' => $projectResult['error'],
|
|
'details' => $projectResult['details'] ?? '',
|
|
'action' => $projectResult['action'] ?? 'Check OMG API configuration',
|
|
'httpCode' => $projectResult['httpCode'] ?? 0,
|
|
'endpoint' => $omgService->config['base_url'] . '/getProject?project_number=' . $campaignNumber,
|
|
'progress' => $progress
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
$projectData = $projectResult['data'];
|
|
$progress[] = ['stage' => 'omg_api', 'status' => 'success', 'message' => 'Retrieved campaign data from OMG'];
|
|
|
|
// STAGE 5: Map Business Unit
|
|
$progress[] = ['stage' => 'business_unit', 'status' => 'processing', 'message' => 'Mapping business unit...'];
|
|
|
|
$businessArea = $omgService->getBusinessArea($projectData);
|
|
|
|
if (empty($businessArea)) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'stage' => 'business_unit',
|
|
'error' => 'Business area not found in OMG response',
|
|
'details' => 'The OMG API response did not contain a business_area field',
|
|
'action' => 'Check if campaign data is complete in OMG system',
|
|
'omgResponse' => $projectData,
|
|
'progress' => $progress
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
$mappingResult = $omgService->mapBusinessUnit($businessArea);
|
|
|
|
if (!$mappingResult['success']) {
|
|
// Non-fatal warning - continue with "ERROR" value
|
|
$progress[] = [
|
|
'stage' => 'business_unit',
|
|
'status' => 'warning',
|
|
'message' => 'Business unit "' . $businessArea . '" not recognized - using "ERROR"',
|
|
'warning' => $mappingResult['error']
|
|
];
|
|
$businessUnit = 'ERROR';
|
|
} else {
|
|
$businessUnit = $mappingResult['businessUnit'];
|
|
$progress[] = [
|
|
'stage' => 'business_unit',
|
|
'status' => 'success',
|
|
'message' => 'Business unit: ' . $businessUnit
|
|
];
|
|
}
|
|
|
|
// STAGE 6: Transform Data
|
|
$progress[] = ['stage' => 'transform', 'status' => 'processing', 'message' => 'Transforming data for 16 markets...'];
|
|
|
|
$transformResult = $transformer->transformData($parseResult['rows'], $campaignNumber, $businessUnit);
|
|
|
|
if (!$transformResult['success']) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'stage' => 'transform',
|
|
'error' => 'Data transformation failed',
|
|
'details' => count($transformResult['errors']) . ' error(s) found',
|
|
'errors' => $transformResult['errors'],
|
|
'warnings' => $transformResult['warnings'],
|
|
'action' => 'Review errors and fix CSV data',
|
|
'progress' => $progress
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
$outputRowCount = $transformResult['outputRowCount'];
|
|
$progress[] = [
|
|
'stage' => 'transform',
|
|
'status' => 'success',
|
|
'message' => "Transformed {$rowCount} rows into {$outputRowCount} regional rows"
|
|
];
|
|
|
|
// Generate CSV output
|
|
use League\Csv\Writer;
|
|
|
|
$csv = Writer::createFromString();
|
|
$csv->insertOne(array_keys($transformResult['rows'][0])); // Headers
|
|
$csv->insertAll($transformResult['rows']); // Data
|
|
|
|
$csvContent = $csv->toString();
|
|
|
|
// Generate filename (use first ISO code for single file preview)
|
|
$outputFilename = $transformer->generateFilename($campaignNumber, $businessUnit, $transformer->config['iso_codes'][0]);
|
|
|
|
// Store in session for later upload
|
|
session_start();
|
|
$_SESSION['processed_csv'] = [
|
|
'content' => $csvContent,
|
|
'filename' => $outputFilename,
|
|
'campaignNumber' => $campaignNumber,
|
|
'businessUnit' => $businessUnit,
|
|
'rowCount' => $outputRowCount
|
|
];
|
|
|
|
// Return success with preview data
|
|
echo json_encode([
|
|
'success' => true,
|
|
'stage' => 'complete',
|
|
'data' => [
|
|
'inputRows' => $rowCount,
|
|
'outputRows' => $outputRowCount,
|
|
'campaignNumber' => $campaignNumber,
|
|
'businessUnit' => $businessUnit,
|
|
'filename' => $outputFilename,
|
|
'preview' => array_slice($transformResult['rows'], 0, 50), // First 50 rows for preview
|
|
'csvContent' => $csvContent,
|
|
'warnings' => $transformResult['warnings']
|
|
],
|
|
'progress' => $progress
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
error_log('Process CSV exception: ' . $e->getMessage());
|
|
error_log('Stack trace: ' . $e->getTraceAsString());
|
|
|
|
echo json_encode([
|
|
'success' => false,
|
|
'stage' => 'exception',
|
|
'error' => 'Unexpected error occurred',
|
|
'details' => $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
}
|