PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.2.0
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.2.0
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / includes / Migration / Src / ClientMigration.php

ClientMigration.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.2.0, at includes/Migration/Src/ClientMigration.php

553 lines 21.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Client Migration for Easy Invoice
4 *
5 * @package EasyInvoice
6 * @subpackage Migration
7 * @since 2.0.0
8 */
9
10 namespace EasyInvoice\Migration\Src;
11
12 /**
13 * Client migration class.
14 *
15 * Handles migration of clients to WordPress users.
16 *
17 * @since 2.0.0
18 */
19 class ClientMigration extends AbstractMigration {
20
21 /**
22 * Migration description.
23 *
24 * @since 2.0.0
25 * @var string
26 */
27 protected $description = 'Migrate clients to WordPress users';
28
29 /**
30 * Check if migration is needed.
31 *
32 * @since 2.0.0
33 * @return bool
34 */
35 public function is_needed(): bool {
36 global $wpdb;
37
38 // Check if any old invoices or quotes have client data that needs migration
39 $count = $wpdb->get_var($wpdb->prepare(
40 "SELECT COUNT(DISTINCT pm.meta_value)
41 FROM {$wpdb->postmeta} pm
42 JOIN {$wpdb->posts} p ON p.ID = pm.post_id
43 WHERE p.post_type IN ('easy-invoice', 'easy-invoice-quotes')
44 AND pm.meta_key IN ('client_email', '_client_email')
45 AND pm.meta_value != ''
46 AND NOT EXISTS (
47 SELECT 1 FROM {$wpdb->users} u
48 WHERE u.user_email = pm.meta_value
49 )"
50 ));
51
52 if ($count > 0) {
53 $this->log(sprintf('Found %d unique client emails that need migration to WordPress users', $count));
54 return true;
55 }
56
57 // Also check for any client data in meta that hasn't been migrated to user meta
58 $count = $wpdb->get_var($wpdb->prepare(
59 "SELECT COUNT(DISTINCT pm.post_id)
60 FROM {$wpdb->postmeta} pm
61 JOIN {$wpdb->posts} p ON p.ID = pm.post_id
62 WHERE p.post_type IN ('easy-invoice', 'easy-invoice-quotes')
63 AND pm.meta_key IN (
64 'client_name', '_client_name',
65 'client_company', '_client_company',
66 'client_address', '_client_address',
67 'client_phone', '_client_phone',
68 'client_vat', '_client_vat'
69 )
70 AND pm.meta_value != ''
71 AND NOT EXISTS (
72 SELECT 1 FROM {$wpdb->postmeta} pm2
73 WHERE pm2.post_id = pm.post_id
74 AND pm2.meta_key IN ('_easy_invoice_client_id', '_easy_invoice_quote_client_id')
75 )"
76 ));
77
78 if ($count > 0) {
79 $this->log(sprintf('Found %d documents with client data that needs migration', $count));
80 return true;
81 }
82
83 return false;
84 }
85
86 /**
87 * Run the migration.
88 *
89 * @since 2.0.0
90 * @return array
91 */
92 public function migrate(): array {
93 try {
94 global $wpdb;
95
96 $this->log('Starting client migration');
97
98 // Get all unique client emails from invoices and quotes
99 $client_emails = $wpdb->get_col($wpdb->prepare(
100 "SELECT DISTINCT pm.meta_value
101 FROM {$wpdb->postmeta} pm
102 JOIN {$wpdb->posts} p ON p.ID = pm.post_id
103 WHERE p.post_type IN ('easy-invoice', 'easy-invoice-quotes')
104 AND pm.meta_key IN ('client_email', '_client_email', 'easy_invoice_client_email', 'easy_invoice_quote_client_email')
105 AND pm.meta_value != ''
106 AND pm.meta_value != '0'
107 AND pm.meta_value IS NOT NULL
108 AND NOT EXISTS (
109 SELECT 1 FROM {$wpdb->users} u
110 WHERE u.user_email = pm.meta_value
111 )"
112 ));
113
114 $migrated_count = 0;
115 $errors = [];
116
117 // Process each unique client email
118 foreach ($client_emails as $email) {
119 // Get all documents for this client
120 $documents = $wpdb->get_results($wpdb->prepare(
121 "SELECT p.ID, p.post_type
122 FROM {$wpdb->posts} p
123 JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
124 WHERE p.post_type IN ('easy-invoice', 'easy-invoice-quotes')
125 AND pm.meta_key IN ('client_email', '_client_email', 'easy_invoice_client_email', 'easy_invoice_quote_client_email')
126 AND pm.meta_value = %s",
127 $email
128 ));
129
130 if (empty($documents)) {
131 continue;
132 }
133
134 // Get client data from the first document
135 $first_doc = $documents[0];
136 $client_data = [
137 'email' => $email,
138 'name' => '',
139 'company' => '',
140 'address' => '',
141 'phone' => '',
142 'vat' => '',
143 'website' => '',
144 'additional_info' => ''
145 ];
146
147 // Try to get client details from meta
148 $meta_keys = [
149 'name' => ['client_name', '_client_name', 'easy_invoice_client_name', 'easy_invoice_quote_client_name', 'name', '_name', 'customer_name', '_customer_name'],
150 'company' => ['client_company', '_client_company', 'easy_invoice_client_company', 'easy_invoice_quote_client_company', 'company', '_company'],
151 'address' => ['client_address', '_client_address', 'easy_invoice_client_address', 'easy_invoice_quote_client_address', 'address', '_address'],
152 'phone' => ['client_phone', '_client_phone', 'easy_invoice_client_phone', 'easy_invoice_quote_client_phone', 'phone', '_phone'],
153 'vat' => ['client_vat', '_client_vat', 'easy_invoice_client_vat', 'easy_invoice_quote_client_vat', 'vat', '_vat'],
154 'website' => ['client_website', '_client_website', 'easy_invoice_client_website', 'easy_invoice_quote_client_website', 'website', '_website'],
155 'additional_info' => ['client_extra_info', '_client_extra_info', 'easy_invoice_client_extra_info', 'easy_invoice_quote_client_extra_info', 'extra_info', '_extra_info', 'additional_info', '_additional_info']
156 ];
157
158 foreach ($meta_keys as $field => $possible_keys) {
159 foreach ($possible_keys as $key) {
160 $value = get_post_meta($first_doc->ID, $key, true);
161 if (!empty($value)) {
162 $client_data[$field] = $value;
163 break;
164 }
165 }
166 }
167
168 // Create or update WordPress user
169 $user_id = $this->get_or_create_user($client_data);
170
171 if ($user_id > 0) {
172 // Update client ID in all documents
173 foreach ($documents as $doc) {
174 if ($doc->post_type === 'easy-invoice') {
175 update_post_meta($doc->ID, '_easy_invoice_client_id', $user_id);
176 // Also update the old meta key for backward compatibility
177 update_post_meta($doc->ID, 'client_email', $client_data['email']);
178 } else {
179 update_post_meta($doc->ID, '_easy_invoice_quote_client_id', $user_id);
180 // Also update the old meta key for backward compatibility
181 update_post_meta($doc->ID, 'client_email', $client_data['email']);
182 }
183
184 // Store mapping for future reference
185 update_option('easy_invoice_client_user_mapping_' . $doc->ID, $user_id);
186 update_user_meta($user_id, '_easy_invoice_original_client_id', $doc->ID);
187 }
188
189 $migrated_count++;
190 $this->log(sprintf('Successfully migrated client with email %s to user ID %d', $email, $user_id));
191 } else {
192 $errors[] = sprintf('Failed to create/update user for client with email %s', $email);
193 }
194 }
195
196 if (empty($errors)) {
197 return [
198 'success' => true,
199 'message' => sprintf('Successfully migrated %d clients', $migrated_count),
200 'count' => $migrated_count
201 ];
202 } else {
203 return [
204 'success' => false,
205 'message' => 'Client migration completed with errors: ' . implode(', ', $errors),
206 'count' => $migrated_count
207 ];
208 }
209
210 } catch (\Exception $e) {
211 $this->log('Client migration failed: ' . $e->getMessage(), 'error');
212 return [
213 'success' => false,
214 'message' => $e->getMessage(),
215 'count' => 0
216 ];
217 }
218 }
219
220 /**
221 * Migrate a single client to WordPress user.
222 *
223 * @since 2.0.0
224 * @param object $client Client post object
225 * @return array
226 */
227 private function migrate_single_client($client): array {
228 try {
229 // Get client meta with fallbacks for different key formats
230 $meta_keys = [
231 'name' => ['client_name', '_client_name'],
232 'email' => ['client_email', '_client_email'],
233 'company' => ['client_company', '_client_company'],
234 'address' => ['client_address', '_client_address'],
235 'phone' => ['client_phone', '_client_phone'],
236 'vat' => ['client_vat', '_client_vat']
237 ];
238
239 $client_data = [];
240 foreach ($meta_keys as $key => $possible_keys) {
241 foreach ($possible_keys as $meta_key) {
242 $value = get_post_meta($client->ID, $meta_key, true);
243 if (!empty($value)) {
244 $client_data[$key] = $value;
245 break;
246 }
247 }
248 }
249
250 // Use post title as name if no name meta found
251 $client_name = $client_data['name'] ?? $client->post_title;
252 $client_email = $client_data['email'] ?? '';
253
254 // If no email, generate a unique one
255 if (empty($client_email)) {
256 $client_email = 'client_' . $client->ID . '@example.com';
257 }
258
259 // Process client name
260 $name_data = $this->process_client_name($client_name);
261
262 // Check if user already exists with this email
263 $existing_user = get_user_by('email', $client_email);
264 if ($existing_user) {
265 // Update user meta
266 $this->update_user_meta($existing_user->ID, [
267 'business_client_name' => $client_name, // Full name as business name
268 'first_name' => $name_data['first_name'],
269 'last_name' => $name_data['last_name'],
270 'company' => $client_data['company'] ?? '',
271 'address' => $client_data['address'] ?? '',
272 'phone' => $client_data['phone'] ?? '',
273 'vat' => $client_data['vat'] ?? '',
274 'website' => $client_data['website'] ?? '',
275 'additional_info' => $client_data['additional_info'] ?? ''
276 ]);
277
278 // Store mapping
279 update_option('easy_invoice_client_user_mapping_' . $client->ID, $existing_user->ID);
280
281 // Store reverse mapping
282 update_user_meta($existing_user->ID, '_easy_invoice_original_client_id', $client->ID);
283
284 $this->log(sprintf('Updated existing user %d for client %d', $existing_user->ID, $client->ID));
285
286 return [
287 'success' => true,
288 'message' => sprintf('Updated existing user %d for client %d', $existing_user->ID, $client->ID),
289 'user_id' => $existing_user->ID
290 ];
291 }
292
293 // Create username from email or name
294 $username = sanitize_user(current(explode('@', $client_email)), true);
295 if (username_exists($username)) {
296 $username = $username . '_' . $client->ID;
297 }
298
299 // Create random password
300 $password = wp_generate_password();
301
302 // Create user
303 $user_data = [
304 'user_login' => $username,
305 'user_email' => $client_email,
306 'user_pass' => $password,
307 'display_name' => $client_name,
308 'role' => 'easy_invoice_client'
309 ];
310
311 $user_id = wp_insert_user($user_data);
312
313 if (is_wp_error($user_id)) {
314 return [
315 'success' => false,
316 'message' => $user_id->get_error_message()
317 ];
318 }
319
320 // Add user meta
321 $this->update_user_meta($user_id, [
322 'business_client_name' => $client_name, // Set the client name as business name
323 'company' => $client_data['company'] ?? '',
324 'address' => $client_data['address'] ?? '',
325 'phone' => $client_data['phone'] ?? '',
326 'vat' => $client_data['vat'] ?? '',
327 'additional_info' => $client_data['additional_info'] ?? ''
328 ]);
329
330 // Store mappings
331 update_option('easy_invoice_client_user_mapping_' . $client->ID, $user_id);
332 update_user_meta($user_id, '_easy_invoice_original_client_id', $client->ID);
333
334 $this->log(sprintf('Created user %d for client %d', $user_id, $client->ID));
335
336 return [
337 'success' => true,
338 'message' => sprintf('Created user %d for client %d', $user_id, $client->ID),
339 'user_id' => $user_id
340 ];
341
342 } catch (\Exception $e) {
343 return [
344 'success' => false,
345 'message' => $e->getMessage()
346 ];
347 }
348 }
349
350 /**
351 * Update user meta with client data.
352 *
353 * @since 2.0.0
354 * @param int $user_id User ID
355 * @param array $meta Meta data
356 * @return void
357 */
358 private function update_user_meta(int $user_id, array $meta): void {
359 foreach ($meta as $key => $value) {
360 if (!empty($value)) {
361 // Map the key to the correct meta key
362 $meta_key = $this->get_meta_key($key);
363 if ($meta_key) {
364 update_user_meta($user_id, $meta_key, $value);
365 }
366 }
367 }
368 }
369
370 /**
371 * Get the correct meta key for a field.
372 *
373 * @since 2.0.0
374 * @param string $field Field name
375 * @return string Meta key
376 */
377 private function get_meta_key(string $field): string {
378 $meta_keys = [
379 'business_client_name' => '_easy_invoice_client_business_client_name',
380 'first_name' => '_easy_invoice_client_first_name',
381 'last_name' => '_easy_invoice_client_last_name',
382 'company' => '_easy_invoice_client_company',
383 'address' => '_easy_invoice_client_address',
384 'phone' => '_easy_invoice_client_phone',
385 'vat' => '_easy_invoice_client_vat',
386 'website' => '_easy_invoice_client_website',
387 'additional_info' => '_easy_invoice_client_extra_info'
388 ];
389
390 return $meta_keys[$field] ?? '';
391 }
392
393 /**
394 * Process client name and split into first and last name.
395 *
396 * @since 2.0.0
397 * @param string $full_name Full client name
398 * @return array Array with first_name and last_name
399 */
400 private function process_client_name(string $full_name): array {
401 $full_name = trim($full_name);
402
403 if (empty($full_name)) {
404 return ['first_name' => '', 'last_name' => ''];
405 }
406
407 // Split by space
408 $name_parts = explode(' ', $full_name);
409
410 if (count($name_parts) === 1) {
411 // Only one name, use as first name
412 return ['first_name' => $name_parts[0], 'last_name' => ''];
413 } elseif (count($name_parts) === 2) {
414 // Two names, first and last
415 return ['first_name' => $name_parts[0], 'last_name' => $name_parts[1]];
416 } else {
417 // More than two names, first name is first part, last name is everything else
418 $first_name = array_shift($name_parts);
419 $last_name = implode(' ', $name_parts);
420 return ['first_name' => $first_name, 'last_name' => $last_name];
421 }
422 }
423
424 /**
425 * Get user ID for client.
426 *
427 * @since 2.0.0
428 * @param int $client_id Old client ID
429 * @return int User ID or 0 if not found
430 */
431 /**
432 * Get or create a WordPress user for a client.
433 *
434 * @since 2.0.0
435 * @param array $client_data Client data array with email, name, company, address, phone, vat
436 * @return int User ID or 0 if failed
437 */
438 public function get_or_create_user(array $client_data): int {
439 if (empty($client_data['email'])) {
440 return 0;
441 }
442
443 // Try to find existing user by email
444 $user = get_user_by('email', $client_data['email']);
445 if ($user) {
446 // Process client name
447 $name_data = $this->process_client_name($client_data['name'] ?? '');
448
449 // Update user meta
450 $this->update_user_meta($user->ID, [
451 'business_client_name' => $client_data['name'] ?? '', // Full name as business name
452 'first_name' => $name_data['first_name'],
453 'last_name' => $name_data['last_name'],
454 'company' => $client_data['company'] ?? '',
455 'address' => $client_data['address'] ?? '',
456 'phone' => $client_data['phone'] ?? '',
457 'vat' => $client_data['vat'] ?? '',
458 'website' => $client_data['website'] ?? '',
459 'additional_info' => $client_data['additional_info'] ?? ''
460 ]);
461
462 return $user->ID;
463 }
464
465 // Create new user
466 $username = sanitize_user(current(explode('@', $client_data['email'])), true);
467 if (username_exists($username)) {
468 $username = $username . '_' . time();
469 }
470
471 // Process client name
472 $name_data = $this->process_client_name($client_data['name'] ?? '');
473
474 // If no name provided, use email username part
475 $display_name = !empty($client_data['name']) ? $client_data['name'] : current(explode('@', $client_data['email']));
476
477 $user_data = [
478 'user_login' => $username,
479 'user_email' => $client_data['email'],
480 'user_pass' => wp_generate_password(),
481 'display_name' => $display_name,
482 'first_name' => $name_data['first_name'],
483 'last_name' => $name_data['last_name'],
484 'role' => 'easy_invoice_client'
485 ];
486
487 $user_id = wp_insert_user($user_data);
488
489 if (is_wp_error($user_id)) {
490 $this->log(sprintf('Failed to create user for client with email %s: %s', $client_data['email'], $user_id->get_error_message()), 'error');
491 return 0;
492 }
493
494 // Add user meta
495 $this->update_user_meta($user_id, [
496 'business_client_name' => $client_data['name'] ?? '', // Full name as business name
497 'first_name' => $name_data['first_name'],
498 'last_name' => $name_data['last_name'],
499 'company' => $client_data['company'] ?? '',
500 'address' => $client_data['address'] ?? '',
501 'phone' => $client_data['phone'] ?? '',
502 'vat' => $client_data['vat'] ?? '',
503 'website' => $client_data['website'] ?? '',
504 'additional_info' => $client_data['additional_info'] ?? ''
505 ]);
506
507 $this->log(sprintf('Created new user %d for client with email %s', $user_id, $client_data['email']));
508 return $user_id;
509 }
510
511 public function get_user_id_for_client(int $client_id): int {
512 if (empty($client_id)) {
513 return 0;
514 }
515
516 $user_id = get_option('easy_invoice_client_user_mapping_' . $client_id);
517 return !empty($user_id) ? (int)$user_id : 0;
518 }
519
520 /**
521 * Get client meta for user.
522 *
523 * @since 2.0.0
524 * @param int $user_id User ID
525 * @return array Client meta
526 */
527 public function get_client_meta_for_user(int $user_id): array {
528 if (empty($user_id)) {
529 return [];
530 }
531
532 $user = get_user_by('id', $user_id);
533 if (!$user) {
534 return [];
535 }
536
537 return [
538 'first_name' => get_user_meta($user_id, '_easy_invoice_client_first_name', true) ?: $user->first_name,
539 'last_name' => get_user_meta($user_id, '_easy_invoice_client_last_name', true) ?: $user->last_name,
540 'business_client_name' => get_user_meta($user_id, '_easy_invoice_client_business_client_name', true),
541 'email' => get_user_meta($user_id, '_easy_invoice_client_email', true) ?: $user->user_email,
542 'username' => get_user_meta($user_id, '_easy_invoice_client_username', true) ?: $user->user_login,
543 'address' => get_user_meta($user_id, '_easy_invoice_client_address', true),
544 'extra_info' => get_user_meta($user_id, '_easy_invoice_client_extra_info', true),
545 'website' => get_user_meta($user_id, '_easy_invoice_client_website', true) ?: $user->user_url,
546 'display_name' => $user->display_name,
547 'company' => get_user_meta($user_id, '_easy_invoice_client_company', true),
548 'phone' => get_user_meta($user_id, '_easy_invoice_client_phone', true),
549 'vat' => get_user_meta($user_id, '_easy_invoice_client_vat', true)
550 ];
551 }
552 }
553