| | |
| | | protected $wpdb; |
| | | protected string $jane_clients_table; |
| | | protected array $import_stats = []; |
| | | |
| | | protected int $lineNumber = 0; |
| | | protected array $skipped_details = []; |
| | | // CSV column mapping |
| | | protected array $column_map = [ |
| | | 'patient_guid' => 'patient_guid', |
| | |
| | | 'last_name' => 'Last Name', |
| | | 'email' => 'Email', |
| | | ]; |
| | | protected array $headers = []; |
| | | |
| | | public function __construct() |
| | | { |
| | |
| | | * |
| | | * @param string $file_path Path to the CSV file |
| | | * @param array $options Import options (e.g., update_existing, send_welcome_email) |
| | | * @return array Import results with stats and errors |
| | | * @return array|WP_Error Import results with stats and errors |
| | | */ |
| | | public function importFromCSV(string $file_path, array $options = []): array |
| | | public function importFromCSV(string $file_path, array $options = []): array|WP_Error |
| | | { |
| | | $this->skipped_details = []; |
| | | $this->lineNumber = 0; // Reset line number |
| | | |
| | | // Initialize stats |
| | | $this->import_stats = [ |
| | | 'total_rows' => 0, |
| | |
| | | 'updated' => 0, |
| | | 'skipped' => 0, |
| | | 'errors' => [], |
| | | 'unmatched_emails' => [] |
| | | 'unmatched_emails' => [], |
| | | 'skipped_details' => [] // Add this |
| | | ]; |
| | | |
| | | // Validate file exists |
| | |
| | | fclose($handle); |
| | | return new WP_Error('invalid_csv', 'CSV file is empty or invalid'); |
| | | } |
| | | $this->headers = array_map('trim', $headers); |
| | | |
| | | // Map column indices |
| | | $column_indices = $this->mapColumnIndices($headers); |
| | | $column_indices = $this->mapColumnIndices($this->headers); |
| | | if (is_wp_error($column_indices)) { |
| | | fclose($handle); |
| | | return $column_indices; |
| | |
| | | // Process each row |
| | | while (($row = fgetcsv($handle)) !== false) { |
| | | $this->import_stats['total_rows']++; |
| | | $this->lineNumber++; |
| | | |
| | | $result = $this->processClientRow($row, $column_indices, [ |
| | | 'update_existing' => $update_existing, |
| | | 'send_welcome_email' => $send_welcome_email, |
| | | 'create_users' => $create_users |
| | | 'create_users' => $create_users, |
| | | 'default_role' => $options['default_role'] ?? null |
| | | ]); |
| | | |
| | | if (is_wp_error($result)) { |
| | |
| | | |
| | | fclose($handle); |
| | | |
| | | // Add skipped details to stats |
| | | $this->import_stats['skipped_details'] = $this->skipped_details; |
| | | |
| | | return $this->import_stats; |
| | | } |
| | | |
| | |
| | | |
| | | // Validate required fields |
| | | if (empty($patient_guid) || empty($email)) { |
| | | $this->skipped_details[] = [ |
| | | 'name' => $first_name . ' ' . $last_name, |
| | | 'guid' => $patient_guid, |
| | | 'email' => $email, |
| | | 'reason' => 'Missing guid or email', |
| | | 'line' => $this->lineNumber |
| | | ]; |
| | | return new WP_Error('invalid_data', 'Missing patient_guid or email'); |
| | | } |
| | | |
| | | // Sanitize email |
| | | $email = sanitize_email($email); |
| | | if (!is_email($email)) { |
| | | $this->skipped_details[] = [ |
| | | 'name' => $first_name . ' ' . $last_name, |
| | | 'guid' => $patient_guid, |
| | | 'email' => $email, |
| | | 'reason' => 'Invalid Email', |
| | | 'line' => $this->lineNumber |
| | | ]; |
| | | return new WP_Error('invalid_email', 'Invalid email address: ' . $email); |
| | | } |
| | | |
| | | // Check if client already exists in mapping table |
| | | $existing_mapping = $this->getClientByGuid($patient_guid); |
| | | |
| | | // Build full data array with all CSV columns for meta storage |
| | | $data = []; |
| | | foreach ($row as $index => $value) { |
| | | $header = $this->headers[$index] ?? 'unknown_' . $index; |
| | | $data[$header] = trim($value); |
| | | } |
| | | |
| | | // Ensure these keys exist for backward compatibility |
| | | $data['patient_guid'] = $patient_guid; |
| | | $data['First Name'] = $first_name; |
| | | $data['Last Name'] = $last_name; |
| | | $data['Email'] = $email; |
| | | |
| | | // Find or create WordPress user |
| | | $user = get_user_by('email', $email); |
| | | $action = 'existing'; |
| | | |
| | | if (!$user && $options['create_users']) { |
| | | if ($user) { |
| | | if ($options['update_existing']) { |
| | | $this->updateExistingClient($user->ID, $data, $options); |
| | | $action = 'updated'; |
| | | } else { |
| | | $this->skipped_details[] = [ |
| | | 'name' => $first_name . ' ' . $last_name, |
| | | 'email' => $email, |
| | | 'reason' => 'User already exists (update not enabled)', |
| | | 'line' => $this->lineNumber |
| | | ]; |
| | | return new WP_Error('user_exists', 'User already exists'); |
| | | } |
| | | } elseif ($options['create_users']) { |
| | | // Create new user |
| | | $user_id = $this->createWordPressUser($email, $first_name, $last_name, $options['send_welcome_email']); |
| | | $user_id = $this->createClientUser($data, $options); |
| | | |
| | | if (is_wp_error($user_id)) { |
| | | $this->skipped_details[] = [ |
| | | 'name' => $first_name . ' ' . $last_name, |
| | | 'guid' => $patient_guid, |
| | | 'email' => $email, |
| | | 'reason' => $user_id->get_error_message(), |
| | | 'line' => $this->lineNumber |
| | | ]; |
| | | return $user_id; |
| | | } |
| | | |
| | | $user = get_user_by('ID', $user_id); |
| | | $action = 'created'; |
| | | |
| | | } elseif (!$user) { |
| | | } else { |
| | | // User doesn't exist and we're not creating users |
| | | $this->skipped_details[] = [ |
| | | 'name' => $first_name . ' ' . $last_name, |
| | | 'guid' => $patient_guid, |
| | | 'email' => $email, |
| | | 'reason' => 'User not found and create_users is false', |
| | | 'line' => $this->lineNumber |
| | | ]; |
| | | $this->import_stats['unmatched_emails'][] = $email; |
| | | return new WP_Error('user_not_found', 'User not found and create_users is false'); |
| | | |
| | | } else { |
| | | $action = 'existing'; |
| | | } |
| | | |
| | | // Update or insert client mapping |
| | |
| | | 'last_name' => $last_name, |
| | | 'email' => $email |
| | | ]); |
| | | $action = 'updated'; |
| | | } |
| | | } else { |
| | | $this->insertClientMapping([ |
| | |
| | | 'last_name' => $last_name, |
| | | 'email' => $email |
| | | ]); |
| | | if ($action !== 'created') { |
| | | $action = 'mapped'; |
| | | } |
| | | } |
| | | |
| | | return [ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Create a new WordPress user |
| | | * Create a new client user from Jane App data |
| | | * |
| | | * @param string $email User email |
| | | * @param string $first_name First name |
| | | * @param string $last_name Last name |
| | | * @param bool $send_welcome_email Whether to send welcome email |
| | | * @param array $data Client data from CSV |
| | | * @param array $options Import options |
| | | * @return int|WP_Error User ID or error |
| | | */ |
| | | protected function createWordPressUser(string $email, string $first_name, string $last_name, bool $send_welcome_email = false): int|WP_Error |
| | | protected function createClientUser(array $data, array $options) |
| | | { |
| | | $email = sanitize_email($data['Email']); |
| | | $first_name = sanitize_text_field($data['First Name'] ?? ''); |
| | | $last_name = sanitize_text_field($data['Last Name'] ?? ''); |
| | | |
| | | // Generate username from email |
| | | $username = $this->generateUsername($email); |
| | | $username = sanitize_user($email); |
| | | |
| | | // Generate random password |
| | | $password = wp_generate_password(12, true, true); |
| | | // Ensure unique username |
| | | $base_username = $username; |
| | | $counter = 1; |
| | | while (username_exists($username)) { |
| | | $username = $base_username . $counter; |
| | | $counter++; |
| | | } |
| | | |
| | | $userdata = [ |
| | | // Get the role from options with proper fallback |
| | | $role = $options['default_role'] ?? get_option(BASE . 'client_import_role', BASE.'client'); |
| | | |
| | | // Ensure role exists |
| | | if (!get_role($role)) { |
| | | return new WP_Error('invalid_role', 'Invalid role'); |
| | | } |
| | | |
| | | // Create user |
| | | $user_data = [ |
| | | 'user_login' => $username, |
| | | 'user_email' => $email, |
| | | 'user_pass' => $password, |
| | | 'first_name' => $first_name, |
| | | 'last_name' => $last_name, |
| | | 'display_name' => trim($first_name . ' ' . $last_name), |
| | | 'role' => apply_filters(BASE . 'jane_import_default_role', 'customer') |
| | | 'role' => $role, |
| | | 'user_pass' => wp_generate_password(16, true, true) |
| | | ]; |
| | | |
| | | $user_id = wp_insert_user($userdata); |
| | | $user_id = wp_insert_user($user_data); |
| | | |
| | | if (is_wp_error($user_id)) { |
| | | return $user_id; |
| | | } |
| | | |
| | | // Send welcome email if requested |
| | | if ($send_welcome_email) { |
| | | wp_send_new_user_notifications($user_id, 'both'); |
| | | // Store Jane App data as user meta |
| | | $this->storeClientMeta($user_id, $data); |
| | | |
| | | // Send welcome email if enabled |
| | | if ($options['send_welcome_email'] ?? false) { |
| | | wp_new_user_notification($user_id, null, 'user'); |
| | | } |
| | | |
| | | do_action(BASE . 'jane_client_created', $user_id, $userdata); |
| | | |
| | | return $user_id; |
| | | } |
| | | |
| | | /** |
| | | * Store Jane App client data as user meta |
| | | * |
| | | * @param int $user_id |
| | | * @param array $data |
| | | */ |
| | | protected function storeClientMeta(int $user_id, array $data): void |
| | | { |
| | | // Store Jane App specific fields |
| | | if (!empty($data['patient_guid'])) { |
| | | update_user_meta($user_id, BASE . 'jane_patient_guid', sanitize_text_field($data['patient_guid'])); |
| | | } |
| | | |
| | | if (!empty($data['Patient Number'])) { |
| | | update_user_meta($user_id, BASE . 'jane_patient_number', sanitize_text_field($data['Patient Number'])); |
| | | } |
| | | |
| | | if (!empty($data['Member Since'])) { |
| | | update_user_meta($user_id, BASE . 'member_since', sanitize_text_field($data['Member Since'])); |
| | | } |
| | | |
| | | if (!empty($data['Mobile Phone'])) { |
| | | update_user_meta($user_id, BASE . 'phone', sanitize_text_field($data['Mobile Phone'])); |
| | | } |
| | | |
| | | if (!empty($data['Birth Date'])) { |
| | | update_user_meta($user_id, BASE . 'birth_date', sanitize_text_field($data['Birth Date'])); |
| | | } |
| | | |
| | | if (!empty($data['Referral Source'])) { |
| | | update_user_meta($user_id, BASE . 'referral_source', sanitize_text_field($data['Referral Source'])); |
| | | } |
| | | |
| | | // Store full Jane App data as JSON for reference |
| | | update_user_meta($user_id, BASE . 'jane_import_data', $data); |
| | | update_user_meta($user_id, BASE . 'jane_import_date', current_time('mysql')); |
| | | } |
| | | |
| | | /** |
| | | * Update existing client with Jane App data |
| | | * |
| | | * @param int $user_id |
| | | * @param array $data |
| | | * @param array $options |
| | | */ |
| | | protected function updateExistingClient(int $user_id, array $data, array $options): void |
| | | { |
| | | // Update user fields if they're empty |
| | | $user_data = ['ID' => $user_id]; |
| | | |
| | | $current_user = get_user_by('ID', $user_id); |
| | | |
| | | if (empty($current_user->first_name) && !empty($data['First Name'])) { |
| | | $user_data['first_name'] = sanitize_text_field($data['First Name']); |
| | | } |
| | | |
| | | if (empty($current_user->last_name) && !empty($data['Last Name'])) { |
| | | $user_data['last_name'] = sanitize_text_field($data['Last Name']); |
| | | } |
| | | |
| | | if (count($user_data) > 1) { |
| | | wp_update_user($user_data); |
| | | } |
| | | |
| | | // Always update meta data |
| | | $this->storeClientMeta($user_id, $data); |
| | | } |
| | | |
| | | /** |
| | | * Generate unique username from email |
| | | * |
| | | * @param string $email Email address |