PluginProbe
AliNext – WooCommerce Dropshipping Plugin for AliExpress / trunk
AliNext – WooCommerce Dropshipping Plugin for AliExpress vtrunk
ali2woo-lite / includes / classes / controller / ImportAjaxController.php

ImportAjaxController.php in AliNext – WooCommerce Dropshipping Plugin for AliExpress trunk, at includes/classes/controller/ImportAjaxController.php

1,454 lines 59.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Description of ImportAjaxController
5 *
6 * @author Ali2Woo Team
7 *
8 * @autoload: a2wl_admin_init
9 *
10 * @ajax: true
11 */
12 // phpcs:ignoreFile WordPress.Security.EscapeOutput.OutputNotEscaped
13 namespace AliNext_Lite;;
14
15 use Exception;
16 use Pages;
17 use Throwable;
18
19 class ImportAjaxController extends AbstractController
20 {
21 protected ProductImport $ProductImportModel;
22 protected Woocommerce $WoocommerceModel;
23 protected ProductReviewsService $ProductReviewsService;
24 protected Override $OverrideModel;
25 protected Aliexpress $AliexpressModel;
26 protected SplitProductService $SplitProductService;
27
28 protected ProductShippingDataService $ProductShippingDataService;
29 protected ImportListService $ImportListService;
30 protected ProductService $ProductService;
31
32 protected PriceFormulaService $PriceFormulaService;
33 protected ImportedProductServiceFactory $ImportedProductServiceFactory;
34 protected WoocommerceService $WoocommerceService;
35 protected WoocommerceCategoryService $WoocommerceCategoryService;
36 protected BackgroundProcessFactory $BackgroundProcessFactory;
37 protected ProductImportTransactionService $ProductImportTransactionService;
38
39 public function __construct(
40 ProductImport $ProductImportModel, Woocommerce $WoocommerceModel, ProductReviewsService $ProductReviewsService,
41 Override $OverrideModel, Aliexpress $AliexpressModel, SplitProductService $SplitProductService,
42 ProductShippingDataService $ProductShippingDataService, ImportListService $ImportListService,
43 ProductService $ProductService, PriceFormulaService $PriceFormulaService,
44 ImportedProductServiceFactory $ImportedProductServiceFactory, WoocommerceService $WoocommerceService,
45 WoocommerceCategoryService $WoocommerceCategoryService,
46 BackgroundProcessFactory $BackgroundProcessFactory,
47 ProductImportTransactionService $ProductImportTransactionService,
48 ) {
49 parent::__construct();
50
51 $this->ProductImportModel = $ProductImportModel;
52 $this->WoocommerceModel = $WoocommerceModel;
53 $this->ProductReviewsService = $ProductReviewsService;
54 $this->OverrideModel = $OverrideModel;
55 $this->AliexpressModel = $AliexpressModel;
56 $this->SplitProductService = $SplitProductService;
57 $this->ProductShippingDataService = $ProductShippingDataService;
58 $this->ImportListService = $ImportListService;
59 $this->ProductService = $ProductService;
60 $this->PriceFormulaService = $PriceFormulaService;
61 $this->ImportedProductServiceFactory = $ImportedProductServiceFactory;
62 $this->WoocommerceService = $WoocommerceService;
63 $this->WoocommerceCategoryService = $WoocommerceCategoryService;
64 $this->BackgroundProcessFactory = $BackgroundProcessFactory;
65 $this->ProductImportTransactionService = $ProductImportTransactionService;
66
67 add_filter('a2wl_woocommerce_after_add_product', array($this, 'woocommerce_after_add_product'), 30, 4);
68 add_action('wp_ajax_a2wl_push_product', [$this, 'ajax_push_product']);
69 add_action('wp_ajax_a2wl_delete_import_products', [$this, 'ajax_delete_import_products']);
70 add_action('wp_ajax_a2wl_update_product_info', [$this, 'ajax_update_product_info']);
71 add_action('wp_ajax_a2wl_link_to_category', [$this, 'ajax_link_to_category']);
72 add_action('wp_ajax_a2wl_link_to_aliexpress_category', [$this, 'ajax_link_to_aliexpress_category']);
73 add_action('wp_ajax_a2wl_get_all_products_to_import', [$this, 'ajax_get_all_products_to_import']);
74 add_action('wp_ajax_a2wl_get_product', [$this, 'ajax_get_product']);
75 add_action('wp_ajax_a2wl_split_product', [$this, 'ajax_split_product']);
76 add_action('wp_ajax_a2wl_import_images_action', [$this, 'ajax_import_images_action']);
77 add_action('wp_ajax_a2wl_import_cancel_images_action', [$this, 'ajax_import_cancel_images_action']);
78 add_action('wp_ajax_a2wl_search_tags', [$this, 'ajax_search_tags']);
79 add_action('wp_ajax_a2wl_search_products', [$this, 'ajax_search_products']);
80 add_action('wp_ajax_a2wl_override_product', [$this, 'ajax_override_product']);
81 add_action('wp_ajax_a2wl_override_variations', [$this, 'ajax_override_variations']);
82 add_action('wp_ajax_a2wl_cancel_override_product', [$this, 'ajax_cancel_override_product']);
83 add_action('wp_ajax_a2wl_add_to_import', [$this, 'ajax_add_to_import']);
84
85 add_action('wp_ajax_a2wl_remove_from_import', [$this, 'ajax_remove_from_import']);
86 add_action('wp_ajax_a2wl_load_shipping_info', [$this, 'ajax_load_shipping_info']);
87 add_action('wp_ajax_a2wl_set_shipping_info', [$this, 'ajax_set_shipping_info']);
88 add_action('wp_ajax_a2wl_update_shipping_list', [$this, 'ajax_update_shipping_list']);
89 }
90
91 public function woocommerce_after_add_product($result, $product_id, $product, $params)
92 {
93 $product_import_model = new ProductImport();
94 // remove product from process list and from import list
95 $product_import_model->del_product($product['import_id'], true);
96 $product_import_model->del_product($product['import_id']);
97 return $result;
98 }
99
100 public function ajax_push_product(): void
101 {
102 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
103
104 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
105 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
106 echo wp_json_encode($result);
107 wp_die();
108 }
109
110 a2wl_init_error_handler();
111
112 $result = ResultBuilder::buildOk();
113
114 $product_import_model = $this->ProductImportModel;
115 $woocommerce_model = $this->WoocommerceModel;
116
117 $background_import = get_setting('background_import', true);
118
119 if ($background_import) {
120 // NEW import method (in background)
121 if (isset($_POST['id']) && $_POST['id']) {
122 $product = $product_import_model->get_product($_POST['id']);
123 if ($product) {
124 try {
125 $ts = microtime(true);
126
127 $steps = $woocommerce_model->build_steps($product);
128
129 // process first step
130 $result = $woocommerce_model->add_product($product, array('step' => 'init'));
131 unset($steps[array_search('init', $steps)]);
132
133 if ($result['state'] !== 'error') {
134 // write first step log
135 a2wl_info_log("IMPORT[time: " . (microtime(true) - $ts) . ", id:" . $result['product_id'] . ", extId: " . $_POST['id'] . ", step: init]");
136
137 // move product to processing list
138 $product_import_model->move_to_processing($_POST['id']);
139
140 // process all other steps
141 $product_queue = ImportProcess::create_new_queue($result['product_id'], $_POST['id'], $steps, false);
142 if (get_setting('load_review')) {
143 ImportProcess::create_new_queue($result['product_id'], $_POST['id'], array('reviews'), false);
144 }
145
146 $product_queue->dispatch();
147 }
148 } catch (Throwable $e) {
149 a2wl_print_throwable($e);
150 $result = ResultBuilder::buildError($e->getMessage());
151 }
152 } else {
153 $result = ResultBuilder::buildError("Product " . $_POST['id'] . " not find.");
154 }
155 } else {
156 $result = ResultBuilder::buildError("import_product: waiting for ID...");
157 }
158 } else {
159 // Old import method (non-background import method)
160 $this->prepareSystemForImport();
161
162 try {
163 if (isset($_POST['id']) && $_POST['id']) {
164 $product = $product_import_model->get_product($_POST['id']);
165
166 if ($product) {
167 $import_wc_product_id = $woocommerce_model->get_product_id_by_import_id($product['import_id']);
168
169 if (!get_setting('allow_product_duplication') && $import_wc_product_id) {
170 $result = $woocommerce_model->upd_product($import_wc_product_id, $product);
171 } else {
172 $result = $woocommerce_model->add_product($product);
173 }
174
175 $product_id = false;
176 if ($result['state'] !== 'error') {
177 $product_id = $result['product_id'];
178 $product_import_model->del_product($_POST['id']);
179 $result = ResultBuilder::buildOk(['product_id' => $product_id]);
180 } else {
181 $result = ResultBuilder::buildError($result['message']);
182 }
183
184 if ($result['state'] !== 'error' && get_setting('load_review')) {
185 $this->ProductReviewsService->loadReviewsForProductIds([$product_id]);
186 //make sure that post comment status is 'open'
187 wp_update_post(array('ID' => $product_id, 'comment_status' => 'open'));
188 }
189 if ($result['state'] === 'error') {
190 $result = ResultBuilder::buildError($result['message']);
191 }
192
193 } else {
194 $result = ResultBuilder::buildError("Product " . $_POST['id'] . " not find.");
195 }
196 } else {
197 $result = ResultBuilder::buildError("import_product: waiting for ID...");
198 }
199
200 restore_error_handler();
201 } catch (Throwable $e) {
202 a2wl_print_throwable($e);
203 $result = ResultBuilder::buildError($e->getMessage());
204 }
205 }
206
207 echo wp_json_encode($result);
208 wp_die();
209 }
210
211 public function ajax_delete_import_products(): void
212 {
213 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
214
215 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
216 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
217 echo wp_json_encode($result);
218 wp_die();
219 }
220
221 a2wl_init_error_handler();
222 try {
223 if (isset($_POST['ids']) && $_POST['ids']) {
224 $product_import_model = $this->ProductImportModel;
225 $product_import_model->del_product($_POST['ids']);
226 }
227 $result = ResultBuilder::buildOk();
228 restore_error_handler();
229 } catch (Throwable $e) {
230 a2wl_print_throwable($e);
231 $result = ResultBuilder::buildError($e->getMessage());
232 }
233
234 echo wp_json_encode($result);
235 wp_die();
236 }
237
238 public function ajax_update_product_info(): void
239 {
240 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
241
242 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
243 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
244 echo wp_json_encode($result);
245 wp_die();
246 }
247
248 a2wl_init_error_handler();
249 try {
250 $product_import_model = $this->ProductImportModel;
251 $out_data = array();
252 if (isset($_POST['id']) && $_POST['id'] && ($product = $product_import_model->get_product($_POST['id']))) {
253 if (isset($_POST['title']) && $_POST['title']) {
254 if (!isset($product['original_title'])) {
255 $product['original_title'] = $product['title'];
256 }
257 // $product['title'] = stripslashes($_POST['title']);
258 $product['title'] = sanitize_text_field($_POST['title']);
259 }
260
261 if (!empty($_POST['sku'])) {
262 $product['sku'] = stripslashes($_POST['sku']);
263 }
264
265 if (isset($_POST['type']) && $_POST['type'] && in_array($_POST['type'], array('simple', 'external'))) {
266 $product['product_type'] = $_POST['type'];
267 }
268
269 if (isset($_POST['status']) && $_POST['status'] && in_array($_POST['status'], array('publish', 'draft'))) {
270 $product['product_status'] = $_POST['status'];
271 }
272
273 if (isset($_POST['tags']) && $_POST['tags']) {
274 $product['tags'] = $_POST['tags'] ? array_map('sanitize_text_field', $_POST['tags']) : array();
275 }
276
277 if (!empty($_POST['attr_names'])) {
278 foreach ($_POST['attr_names'] as $attr) {
279 foreach ($product['sku_products']['attributes'] as &$product_attr) {
280 if ($product_attr['id'] == $attr['id']) {
281 if (!isset($product_attr['original_name'])) {
282 $product_attr['original_name'] = $product_attr['name'];
283 }
284 $product_attr['name'] = $attr['value'];
285 break;
286 }
287 }
288 }
289 }
290
291 if (isset($_POST['categories'])) {
292 $product['categories'] = array();
293 if ($_POST['categories']) {
294 foreach ($_POST['categories'] as $cat_id) {
295 if (intval($cat_id)) {
296 $product['categories'][] = intval($cat_id);
297 }
298 }
299 }
300
301 }
302
303 if (isset($_POST['description'])) {
304 $product['description'] = stripslashes(trim(urldecode($_POST['description'])));
305 }
306
307 if (isset($_POST['skip_vars']) && $_POST['skip_vars']) {
308 $product['skip_vars'] = $_POST['skip_vars'];
309 }
310
311 if (isset($_POST['reset_skip_vars']) && $_POST['reset_skip_vars']) {
312 $product['skip_vars'] = array();
313 }
314
315 if (isset($_POST['skip_images']) && $_POST['skip_images']) {
316 $product['skip_images'] = $_POST['skip_images'];
317 }
318
319 if (!empty($_POST['no_skip'])) {
320 $product['skip_images'] = array();
321 }
322
323 if (isset($_POST['thumb'])) {
324 $product['thumb_id'] = $_POST['thumb'];
325 }
326
327 if (isset($_POST['specs'])) {
328 $product['attribute'] = array();
329 $split_attribute_values = get_setting('split_attribute_values');
330 $attribute_values_separator = get_setting('attribute_values_separator');
331 foreach ($_POST['specs'] as $attr) {
332 $name = trim($attr['name']);
333 if (!empty($name)) {
334 $el = array('name' => $name);
335 if ($split_attribute_values) {
336 $el['value'] = array_map('trim', explode($attribute_values_separator, $attr['value']));
337 } else {
338 $el['value'] = array($attr['value']);
339 }
340 $product['attribute'][] = $el;
341 }
342 }
343 } else if (!empty($_POST['cleanSpecs'])) {
344 $product['attribute'] = array();
345 }
346
347 if (isset($_POST['disable_var_price_change'])) {
348 if (intval($_POST['disable_var_price_change'])) {
349 $product['disable_var_price_change'] = true;
350 } else {
351 $product['disable_var_price_change'] = false;
352 }
353 }
354
355 if (isset($_POST['disable_var_quantity_change'])) {
356 if (intval($_POST['disable_var_quantity_change'])) {
357 $product['disable_var_quantity_change'] = true;
358 } else {
359 $product['disable_var_quantity_change'] = false;
360 }
361 }
362
363 if (!empty($_POST['variations'])) {
364 $out_data['new_attr_mapping'] = array();
365 foreach ($_POST['variations'] as $variation) {
366 foreach ($product['sku_products']['variations'] as &$v) {
367 if ($v['id'] == $variation['variation_id']) {
368 if (isset($variation['regular_price'])) {
369 $v['calc_regular_price'] = floatval($variation['regular_price']);
370 }
371 if (isset($variation['price'])) {
372 $v['calc_price'] = floatval($variation['price']);
373 }
374 if (isset($variation['quantity'])) {
375 $v['quantity'] = intval($variation['quantity']);
376 }
377
378 if (isset($variation['sku']) && $variation['sku']) {
379 $v['sku'] = sanitize_text_field($variation['sku']);
380 }
381
382 if (isset($variation['attributes']) && is_array($variation['attributes'])) {
383 foreach ($variation['attributes'] as $a) {
384 foreach ($v['attributes'] as $i => $av) {
385 $_attr_val = false;
386 foreach ($product['sku_products']['attributes'] as $tmp_attr) {
387 if (isset($tmp_attr["value"][$av])) {
388 $_attr_val = $tmp_attr["value"][$av];
389 break;
390 }
391 }
392 $old_name = sanitize_text_field($_attr_val['name']);
393 $new_name = sanitize_text_field($a['value']);
394 if ($old_name !== $new_name && $_attr_val['id'] == $a['id']) {
395 $_attr_id = explode(':', $av);
396 $attr_id = $_attr_id[0];
397 $new_attr_id = $attr_id . ':' . md5($variation['variation_id'] . $new_name);
398 if ($av !== $new_attr_id) {
399 $out_data['new_attr_mapping'][] = array('variation_id' => $variation['variation_id'], 'old_attr_id' => $av, 'new_attr_id' => $new_attr_id);
400 }
401 foreach ($product['sku_products']['attributes'] as $ind => $orig_attr) {
402 if ($orig_attr['id'] == $attr_id) {
403 if (!isset($orig_attr['value'][$new_attr_id])) {
404 $product['sku_products']['attributes'][$ind]['value'][$new_attr_id] = $product['sku_products']['attributes'][$ind]['value'][$av];
405 if (!isset($product['sku_products']['attributes'][$ind]['value'][$new_attr_id]['original_id'])) {
406 $product['sku_products']['attributes'][$ind]['value'][$new_attr_id]['original_id'] = $product['sku_products']['attributes'][$ind]['value'][$new_attr_id]['id'];
407 }
408 $product['sku_products']['attributes'][$ind]['value'][$new_attr_id]['id'] = $new_attr_id;
409 $product['sku_products']['attributes'][$ind]['value'][$new_attr_id]['name'] = $new_name;
410 if (!isset($product['sku_products']['attributes'][$ind]['value'][$new_attr_id]['src_id'])) {
411 $product['sku_products']['attributes'][$ind]['value'][$new_attr_id]['src_id'] = $av;
412 }
413 }
414 break;
415 }
416 }
417
418 $v['attributes'][$i] = $new_attr_id;
419 $v['attributes_names'][$i] = sanitize_text_field($a['value']);
420 }
421 }
422 }
423 }
424
425 break;
426 }
427 }
428 }
429 }
430
431 $product_import_model->upd_product($product);
432 $result = ResultBuilder::buildOk($out_data);
433 } else {
434 $result = ResultBuilder::buildError("update_product_info: waiting for ID...");
435 }
436
437 restore_error_handler();
438 } catch (Throwable $e) {
439 a2wl_print_throwable($e);
440 $result = ResultBuilder::buildError($e->getMessage());
441 }
442 echo wp_json_encode($result);
443 wp_die();
444 }
445
446 public function ajax_link_to_category(): void
447 {
448 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
449
450 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
451 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
452 echo wp_json_encode($result);
453 wp_die();
454 }
455
456 $product_import_model = $this->ProductImportModel;
457
458 if (!empty($_POST['categories']) && !empty($_POST['ids'])) {
459 $new_categories = is_array($_POST['categories']) ?
460 array_map('intval', $_POST['categories']) :
461 [intval($_POST['categories'])];
462
463 $ids = ($_POST['ids'] === 'all') ?
464 $product_import_model->get_product_id_list() :
465 (is_array($_POST['ids']) ? $_POST['ids'] : [$_POST['ids']]);
466
467 foreach ($ids as $id) {
468 if ($product = $product_import_model->get_product($id)) {
469 $product['categories'] = $new_categories;
470 $product_import_model->upd_product($product);
471 }
472 }
473 set_setting('remember_categories', $new_categories);
474 } else if (empty($_POST['categories'])) {
475 del_setting('remember_categories');
476 }
477
478 echo wp_json_encode(ResultBuilder::buildOk());
479 wp_die();
480 }
481
482 public function ajax_link_to_aliexpress_category(): void
483 {
484 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
485
486 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
487 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
488 echo wp_json_encode($result);
489 wp_die();
490 }
491
492 $ProductImportModel = $this->ProductImportModel;
493
494 $defaultCategoryIds = false;
495 if (!empty($_POST['categories'])) {
496 $defaultCategoryIds = is_array($_POST['categories']) ?
497 array_map('intval', $_POST['categories']) :
498 [intval($_POST['categories'])];
499 }
500
501 if (!empty($_POST['id'])) {
502 $id = intval($_POST['id']);
503
504 if ($Product = $ProductImportModel->get_product($id)) {
505 $aliexpressCategoryId = $Product['category_id'];
506 if ($aliexpressCategoryId) {
507 a2wl_init_error_handler();
508 try {
509 $insertedCategoryIds = $this->WoocommerceCategoryService->loadAliexpressCategory(
510 $aliexpressCategoryId
511 );
512 restore_error_handler();
513 } catch (ApiException $Exception) {
514
515 echo wp_json_encode(
516 ResultBuilder::buildError(
517 $Exception->getMessage(),
518 ['error_code' => $Exception->getCode()]
519 )
520 );
521 wp_die();
522 }
523
524 if (empty($insertedCategoryIds)) {
525 $selectedCategoryIds = $defaultCategoryIds;
526 } else {
527 //todo: now we sent last category as selected category for the product
528 $selectedCategoryIds = [end($insertedCategoryIds)];
529 //but we can put product to parent categories as well for example:
530 //$selectedCategoryIds = $insertedCategoryIds;
531 }
532
533 if ($selectedCategoryIds) {
534 $Product['categories'] = $selectedCategoryIds;
535 $ProductImportModel->upd_product($Product);
536 }
537 }
538 }
539 }
540
541 echo wp_json_encode(ResultBuilder::buildOk());
542 wp_die();
543 }
544
545 public function ajax_get_all_products_to_import(): void
546 {
547 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
548
549 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
550 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
551 echo wp_json_encode($result);
552 wp_die();
553 }
554
555 $product_import_model = $this->ProductImportModel;
556
557 echo wp_json_encode(ResultBuilder::buildOk(
558 ['ids' => $product_import_model->get_product_id_list()]
559 ));
560 wp_die();
561 }
562
563 public function ajax_get_product(): void
564 {
565 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
566
567 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
568 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
569 echo wp_json_encode($result);
570 wp_die();
571 }
572
573 $product_import_model = $this->ProductImportModel;
574
575 if (!empty($_POST['id'])) {
576 if ($product = $product_import_model->get_product($_POST['id'])) {
577 $result = ResultBuilder::buildOk(array('product' => $product));
578 } else {
579 $result = ResultBuilder::buildError("product not found");
580 }
581 } else {
582 $result = ResultBuilder::buildError("get_product: waiting for ID...");
583 }
584
585 echo wp_json_encode($result);
586 wp_die();
587 }
588
589 public function ajax_split_product(): void
590 {
591 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
592
593 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
594 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
595 echo wp_json_encode($result);
596 wp_die();
597 }
598
599 $result = ResultBuilder::buildOk();
600
601 if (!empty($_POST['id']) && !empty($_POST['attr'])) {
602 $productId = $_POST['id'];
603 $attributeId = $_POST['attr'];
604 a2wl_init_error_handler();
605 try {
606 $this->SplitProductService->splitProductByAttribute($productId, $attributeId);
607 restore_error_handler();
608 }
609 catch (ServiceException $Exception) {
610 a2wl_print_throwable($Exception);
611 $result = ResultBuilder::buildError($Exception);
612 }
613 } else if (!empty($_POST['id']) && !empty($_POST['vars'])) {
614 $productId = $_POST['id'];
615 $variationIds = $_POST['vars'];
616 a2wl_init_error_handler();
617 try {
618 $this->SplitProductService->splitProductBySelectedVariants($productId, $variationIds);
619 }
620 catch (ServiceException $Exception) {
621 a2wl_print_throwable($Exception);
622 $result = ResultBuilder::buildError($Exception);
623 }
624 } else {
625 $message = _x("Split product operation: wrong parameters...",
626 'error text', 'ali2woo');
627 $result = ResultBuilder::buildError($message);
628 }
629
630 echo wp_json_encode($result);
631 wp_die();
632 }
633
634 public function ajax_import_images_action(): void
635 {
636 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
637
638 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
639 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
640 echo wp_json_encode($result);
641 wp_die();
642 }
643
644 $product_import_model = $this->ProductImportModel;
645
646 a2wl_init_error_handler();
647 try {
648 if (isset($_POST['id']) && $_POST['id'] && ($product = $product_import_model->get_product($_POST['id'])) && !empty($_POST['source']) && !empty($_POST['type']) && in_array($_POST['source'], array("description", "variant")) && in_array($_POST['type'], array("copy", "move"))) {
649 if (!empty($_POST['images'])) {
650 foreach ($_POST['images'] as $image) {
651 if ($_POST['type'] == 'copy') {
652 $product['tmp_copy_images'][$image] = $_POST['source'];
653 } else if ($_POST['type'] == 'move') {
654 $product['tmp_move_images'][$image] = $_POST['source'];
655 }
656 }
657
658 $product_import_model->upd_product($product);
659 }
660
661 $result = ResultBuilder::buildOk();
662 } else {
663 $result = ResultBuilder::buildError("Error in params");
664 }
665
666 restore_error_handler();
667 } catch (Throwable $e) {
668 a2wl_print_throwable($e);
669 $result = ResultBuilder::buildError($e->getMessage());
670 }
671
672 echo wp_json_encode($result);
673 wp_die();
674 }
675
676 public function ajax_import_cancel_images_action(): void
677 {
678 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
679
680 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
681 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
682 echo wp_json_encode($result);
683 wp_die();
684 }
685
686 $product_import_model = $this->ProductImportModel;
687
688 a2wl_init_error_handler();
689 try {
690 if (isset($_POST['id']) && $_POST['id'] && ($product = $product_import_model->get_product($_POST['id'])) && !empty($_POST['image']) && !empty($_POST['source']) && !empty($_POST['type']) && in_array($_POST['source'], array("description", "variant")) && in_array($_POST['type'], array("copy", "move"))) {
691 if ($_POST['type'] == 'copy') {
692 unset($product['tmp_copy_images'][$_POST['image']]);
693 } else if ($_POST['type'] == 'move') {
694 unset($product['tmp_move_images'][$_POST['image']]);
695 }
696
697 $product_import_model->upd_product($product);
698
699 $result = ResultBuilder::buildOk();
700 } else {
701 $result = ResultBuilder::buildError("Error in params");
702 }
703
704 restore_error_handler();
705 } catch (Throwable $e) {
706 a2wl_print_throwable($e);
707 $result = ResultBuilder::buildError($e->getMessage());
708 }
709
710 echo wp_json_encode($result);
711 wp_die();
712 }
713
714 public function ajax_search_tags(): void
715 {
716 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
717
718 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
719 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
720 echo wp_json_encode($result);
721 wp_die();
722 }
723
724 $woocommerce_model = $this->WoocommerceModel;
725
726 a2wl_init_error_handler();
727 try {
728 $num_in_page = 50;
729 $page = !empty($_REQUEST['page']) ? intval($_REQUEST['page']) : 1;
730 $search = !empty($_REQUEST['search']) ? $_REQUEST['search'] : '';
731 $result = $woocommerce_model->get_product_tags($search);
732 $total_count = count($result);
733 $result = array_slice($result, $num_in_page * ($page - 1), $num_in_page);
734
735 $result = array(
736 'results' => array_map(function ($o) {return array('id' => $o, 'text' => $o);}, $result),
737 'pagination' => array('more' => $num_in_page * ($page - 1) + $num_in_page < $total_count),
738 );
739 restore_error_handler();
740 } catch (Throwable $e) {
741 a2wl_print_throwable($e);
742 $result = ResultBuilder::buildError($e->getMessage());
743 }
744
745 echo wp_json_encode($result);
746 wp_die();
747 }
748
749 public function ajax_search_products(): void
750 {
751 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
752
753 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
754 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
755 echo wp_json_encode($result);
756 wp_die();
757 }
758
759 a2wl_init_error_handler();
760 try {
761 $num_in_page = 20;
762 $page = !empty($_REQUEST['page']) ? intval($_REQUEST['page']) : 1;
763 $search = !empty($_REQUEST['search']) ? $_REQUEST['search'] : '';
764
765 global $wpdb;
766
767 $sql = "SELECT p.ID, p.post_title, pimg.guid as thumb FROM $wpdb->posts p " .
768 "LEFT JOIN $wpdb->postmeta pm ON (p.ID=pm.post_id AND pm.meta_key='_thumbnail_id') " .
769 "LEFT JOIN $wpdb->posts pimg ON (pimg.ID=pm.meta_value) " .
770 "WHERE p.post_type='product' AND p.post_title like %s LIMIT %d, %d";
771
772 $products = $wpdb->get_results(
773 $wpdb->prepare(
774 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
775 $sql,
776 '%' . $wpdb->esc_like($search) . '%',
777 ($page - 1) * $num_in_page,
778 $num_in_page
779 ),
780 ARRAY_A
781 );
782 $products = $products && is_array($products) ? $products : array();
783 $total_count = $wpdb->get_var(
784 $wpdb->prepare(
785 "SELECT count(ID) FROM $wpdb->posts WHERE post_type='product' AND post_title like %s",
786 '%' . $wpdb->esc_like($search) . '%',
787 )
788 );
789 $result = array(
790 'results' => array_map(function ($o) {return array('id' => $o['ID'], 'text' => $o['post_title'], 'thumb' => $o['thumb']);}, $products),
791 'pagination' => array('more' => $num_in_page * ($page - 1) + $num_in_page < $total_count),
792 );
793
794 restore_error_handler();
795 } catch (Throwable $e) {
796 a2wl_print_throwable($e);
797 $result = ResultBuilder::buildError($e->getMessage());
798 }
799
800 echo wp_json_encode($result);
801 wp_die();
802 }
803
804 public function ajax_override_variations(): void
805 {
806 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
807
808 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
809 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
810 echo wp_json_encode($result);
811 wp_die();
812 }
813
814 $product_id = $_REQUEST['product_id'];
815
816 $result = ResultBuilder::buildOk();
817
818 if (!$product_id) {
819 $result = ResultBuilder::buildError("Customize Toolbar…");
820 }
821
822 if ($result['state'] != 'error') {
823 $result['order_variations'] = $this->OverrideModel->find_orders($product_id);
824 $result['variations'] = $this->OverrideModel->find_variations($product_id);
825 }
826
827 echo wp_json_encode($result);
828 wp_die();
829 }
830
831 public function ajax_override_product(): void
832 {
833 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
834
835 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
836 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
837 echo wp_json_encode($result);
838 wp_die();
839 }
840
841 $result = array('state' => 'ok');
842
843 $product_id = $_REQUEST['product_id'];
844 $external_id = $_REQUEST['external_id'];
845
846 $change_supplier = !empty($_REQUEST['change_supplier']) ? filter_var($_REQUEST['change_supplier'], FILTER_VALIDATE_BOOLEAN) : false;
847 $override_images = !empty($_REQUEST['override_images']) ? filter_var($_REQUEST['override_images'], FILTER_VALIDATE_BOOLEAN) : false;
848 $override_title_description = !empty($_REQUEST['override_title_description']) ? filter_var($_REQUEST['override_title_description'], FILTER_VALIDATE_BOOLEAN) : false;
849 $variations = !empty($_REQUEST['variations']) && is_array($_REQUEST['variations']) ? $_REQUEST['variations'] : array();
850
851 if (!$product_id || !$external_id) {
852 $result = array("state" => "error", "message" => "Wrong params.");
853 }
854
855 if ($result['state'] != 'error') {
856 $override_model = $this->OverrideModel;
857 $result = $override_model->override($product_id, $external_id, $change_supplier, $override_images, $override_title_description, $variations);
858 }
859
860 if ($result['state'] != 'error') {
861 $result['button'] = esc_html__('Override', 'ali2woo');
862 }
863
864 echo wp_json_encode($result);
865 wp_die();
866 }
867
868 public function ajax_cancel_override_product(): void
869 {
870 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
871
872 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
873 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
874 echo wp_json_encode($result);
875 wp_die();
876 }
877
878 $external_id = $_REQUEST['external_id'];
879
880 if ($external_id) {
881 $override_model = $this->OverrideModel;
882 $result = $override_model->cancel_override($external_id);
883 } else {
884 $result = array("state" => "error", "message" => "Wrong params.");
885 }
886
887 if ($result['state'] != 'error') {
888 $result['button'] = esc_html__('Push to Shop', 'ali2woo');
889 $result['override_action'] = '<li><a href="#" class="product-card-override-product">' . esc_html__('Select Product to Override', 'ali2woo') . '</a></li>';
890 }
891
892 echo wp_json_encode($result);
893 wp_die();
894 }
895
896 public function ajax_add_to_import(): void
897 {
898 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
899
900 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
901 echo wp_json_encode(ResultBuilder::buildError($this->getErrorTextNoPermissions()));
902 wp_die();
903 }
904
905 $externalId = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : null;
906 $pageRaw = isset($_POST['page']) ? sanitize_text_field($_POST['page']) : '';
907 $apdRaw = isset($_POST['apd']) ? sanitize_text_field($_POST['apd']) : '';
908
909 if (!$externalId) {
910 $this->respondWithError("add_to_import: waiting for ID...");
911 }
912
913 $priority = $pageRaw === 'a2wl_store'
914 ? ProductSelectorService::PRIORITY_STORE_FIRST
915 : ProductSelectorService::PRIORITY_RESULT_FIRST;
916
917 $apd = !empty($apdRaw)
918 ? ['data' => ['apd' => json_decode(stripslashes($apdRaw))]]
919 : [];
920
921 a2wl_init_error_handler();
922 try {
923 $result = $this->ProductImportTransactionService->execute($externalId, $priority, $apd);
924 restore_error_handler();
925 }
926 catch (Exception $Exception) {
927 $this->respondWithError($Exception->getMessage());
928 }
929
930 echo wp_json_encode($result->status === 'ok'
931 ? ResultBuilder::buildOk()
932 : ResultBuilder::buildError($result->message)
933 );
934 wp_die();
935 }
936
937 public function ajax_remove_from_import(): void
938 {
939 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
940
941 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
942 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
943 echo wp_json_encode($result);
944 wp_die();
945 }
946
947 if (isset($_POST['id'])) {
948 $product = false;
949
950 if ($_POST['page'] === 'a2wl_dashboard'){
951 $products = a2wl_get_transient('a2wl_search_result');
952 } elseif ($_POST['page'] === 'a2wl_store'){
953 $products = a2wl_get_transient('a2wl_search_store_result');
954 }
955
956 $product_import_model = $this->ProductImportModel;
957
958 foreach ($products as $p) {
959 if ($p['id'] == $_POST['id']) {
960 $product = $p;
961 break;
962 }
963 }
964 if ($product) {
965 $product_import_model->del_product($product[ImportedProductService::FIELD_EXTERNAL_PRODUCT_ID]);
966 echo wp_json_encode(ResultBuilder::buildOk());
967 } else {
968 echo wp_json_encode(ResultBuilder::buildError("Product not found in search result"));
969 }
970 } else {
971 echo wp_json_encode(ResultBuilder::buildError("remove_from_import: waiting for ID..."));
972 }
973
974 wp_die();
975 }
976
977 /**
978 * todo: need to refactor this method and split to use service instead of private methods
979 * @return void
980 */
981 public function ajax_load_shipping_info(): void
982 {
983 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
984
985 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
986 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
987 echo wp_json_encode($result);
988 wp_die();
989 }
990
991 if (A2WL()->isFreePlugin()) {
992 $errorText = '<div class="_a2wfo a2wl-info"><div>This feature is available in full version of the plugin.</div><a href="https://ali2woo.com/pricing/?utm_source=lite&utm_medium=lite_banner&utm_campaign=alinext-lite" target="_blank" class="btn">GET FULL VERSION</a></div>';
993 $result = ResultBuilder::buildError($errorText);
994
995 echo wp_json_encode($result);
996 wp_die();
997 }
998
999 if (!Account::getInstance()->get_purchase_code()) {
1000 $errorText = _x('Input your purchase code in the plugin settings', 'error', 'ali2woo');
1001 $result = ResultBuilder::buildError($errorText);
1002
1003 echo wp_json_encode($result);
1004 wp_die();
1005 }
1006
1007 if (isset($_POST['id'])) {
1008 $page = $_POST['page'] ?? 'a2wl_dashboard';
1009
1010 if ($page === 'product') {
1011 $this->ajax_load_shipping_info_for_product();
1012 }
1013
1014 if ($page === 'fulfillment') {
1015 $this->ajax_load_shipping_info_for_fulfillment();
1016 }
1017
1018 if ($page === 'import') {
1019 $this->ajax_load_shipping_info_for_import();
1020 }
1021 }
1022
1023 //don't return shipping items for a2wl_dashboard and a2wl_store pages
1024 echo wp_json_encode(ResultBuilder::buildOk(['products' => []]));
1025 wp_die();
1026 }
1027
1028 /**
1029 * Load shipping info at Woo orders page
1030 * @return void
1031 */
1032 private function ajax_load_shipping_info_for_fulfillment(): void
1033 {
1034 $externalSkuId = $_POST['variation_key'] ?? null;
1035 $wcProductId = intval($_POST['id']);
1036
1037 $quantity = $_POST['quantity'] ?? null;
1038 $quantity = max(1, (int) ($quantity ?? 0));
1039
1040 $WC_ProductOrVariation = wc_get_product($wcProductId);
1041 if (!$WC_ProductOrVariation) {
1042 $errorText = _x('Bad product ID', 'error', 'ali2woo');
1043 $result = ResultBuilder::buildError($errorText);
1044
1045 echo wp_json_encode($result);
1046 wp_die();
1047 }
1048
1049 $wcVariationId = null;
1050
1051 if ($externalSkuId) {
1052 $wcVariationId = $this->WoocommerceModel
1053 ->getProductIdByExternalSkuId($wcProductId, $externalSkuId);
1054 }
1055
1056 if ($externalSkuId && !$wcVariationId) {
1057 $errorText = _x('No such a product with this variation key', 'error', 'ali2woo');
1058 $result = ResultBuilder::buildError($errorText);
1059
1060 echo wp_json_encode($result);
1061 wp_die();
1062 }
1063
1064 if ($wcVariationId) {
1065 $WC_ProductOrVariation = wc_get_product($wcVariationId);
1066 if (!$WC_ProductOrVariation) {
1067 $errorText = _x('Bad product variation ID', 'error', 'ali2woo');
1068
1069 echo wp_json_encode(ResultBuilder::buildError($errorText));
1070 wp_die();
1071 }
1072 }
1073
1074 try {
1075 $importedProduct = $this->WoocommerceService->getProductWithVariations($wcProductId);
1076 } catch (RepositoryException|ServiceException $Exception) {
1077 $errorText = _x('Product doesn`t have imported data from AliExpress', 'error', 'ali2woo');
1078 $result = ResultBuilder::buildError($errorText);
1079
1080 echo wp_json_encode($result);
1081 wp_die();
1082 }
1083
1084 $product_country_to = !empty($importedProduct[ImportedProductService::FIELD_COUNTRY_TO])
1085 ? $importedProduct[ImportedProductService::FIELD_COUNTRY_TO]
1086 : '';
1087
1088 $countryToCode = $_POST['country_to'] ?? $product_country_to;
1089 // $countryFromCode = !empty($_POST['country_from']) ? $_POST['country_from'] : $product_country_from;
1090 $countryFromCode = $this->WoocommerceService->getShippingFromByProduct($WC_ProductOrVariation);
1091
1092 //todo: $externalSkuId should be update if the product variations are changed
1093 $externalSkuId = $_POST['variation_key'] ?? '';
1094
1095 $countryCode = ProductShippingData::meta_key($countryFromCode, $countryToCode);
1096 //need fresh shipping items data on the fulfillment page, clean cache
1097 $importedProduct[ImportedProductService::FIELD_SHIPPING_INFO][$countryCode] = [];
1098
1099 try {
1100 $importedProduct = $this->ProductService->updateProductShippingInfo(
1101 $importedProduct, $countryFromCode,
1102 $countryToCode, $externalSkuId, null,
1103 $quantity
1104 );
1105
1106 $shippingItems = $this->ProductService->getShippingItems(
1107 $importedProduct, $countryToCode, $countryFromCode
1108 );
1109 } catch (ServiceException $ServiceException) {
1110 $shippingItems = [];
1111 $response = ResultBuilder::buildError($ServiceException->getMessage());
1112 echo wp_json_encode($response);
1113 wp_die();
1114 }
1115
1116 if ($quantity < 2) {
1117 // Keep a persistent cache simple: save only for quantity = 1.
1118 try {
1119 $this->ProductShippingDataService->saveItems(
1120 $wcProductId, $countryFromCode, $countryToCode,
1121 $importedProduct[ImportedProductService::FIELD_SHIPPING_INFO][$countryCode]
1122 );
1123 } catch (RepositoryException $RepositoryException) {
1124 a2wl_error_log('Can`t update product shipping items cache' . $RepositoryException->getMessage());
1125 }
1126 }
1127
1128 $importedProduct = $this->PriceFormulaService->applyFormula($importedProduct);
1129
1130 $variationList = [];
1131 if (isset($importedProduct['sku_products']['variations'])) {
1132 foreach ($importedProduct['sku_products']['variations'] as $variation) {
1133 $variationList[] = [
1134 'id' => $variation[ImportedProductService::FIELD_EXTERNAL_SKU_ID],
1135 'calc_price' => $variation['calc_price'],
1136 'calc_regular_price' => $variation['calc_regular_price'],
1137 'title' => $variation['title'],
1138 'ship_from' => $variation[ImportedProductService::FIELD_COUNTRY_CODE],
1139 ];
1140 }
1141 }
1142
1143 $result[] = [
1144 'product_id' => $wcProductId,
1145 'default_method' => $importedProduct[ImportedProductService::FIELD_METHOD] ?? '',
1146 'items' => $shippingItems,
1147 'shipping_cost' => $product[ImportedProductService::FIELD_COST] ?? '',
1148 'variations' => $variationList,
1149 'variation_key' => $externalSkuId,
1150 ];
1151
1152 echo wp_json_encode(ResultBuilder::buildOk(['products' => $result]));
1153 wp_die();
1154 }
1155
1156 /**
1157 * Load shipping info at ProductDataTab (Woo product editing page)
1158 * @return void
1159 */
1160 private function ajax_load_shipping_info_for_product(): void
1161 {
1162 $externalSkuId = $_POST['variation_key'] ?? null;
1163 $wcProductId = intval($_POST['id']);
1164
1165 $WC_ProductOrVariation = wc_get_product($wcProductId);
1166 if (!$WC_ProductOrVariation) {
1167 echo wp_json_encode(ResultBuilder::buildError("Bad product ID"));
1168 wp_die();
1169 }
1170
1171 $wcVariationId = null;
1172
1173 if ($externalSkuId) {
1174 $wcVariationId = $this->WoocommerceModel
1175 ->getProductIdByExternalSkuId($wcProductId, $externalSkuId);
1176 }
1177
1178 if ($externalSkuId && !$wcVariationId) {
1179 $errorText = esc_html__('No such a product with this variation key', 'ali2woo');
1180 $result = ResultBuilder::buildError($errorText);
1181
1182 echo wp_json_encode($result);
1183 wp_die();
1184 }
1185
1186 if ($wcVariationId) {
1187 $WC_ProductOrVariation = wc_get_product($wcVariationId);
1188 if (!$WC_ProductOrVariation) {
1189 echo wp_json_encode(ResultBuilder::buildError("Bad product variation ID"));
1190 wp_die();
1191 }
1192 }
1193
1194 try {
1195 $importedProduct = $this->WoocommerceService->getProduct($wcProductId);
1196 } catch (RepositoryException|ServiceException $Exception) {
1197 $errorText = esc_html__(
1198 'This WooCommerce product does not have imported data from AliExpress', 'ali2woo'
1199 );
1200 $result = ResultBuilder::buildError($errorText);
1201
1202 echo wp_json_encode($result);
1203 wp_die();
1204 }
1205
1206 //todo: Here we take product countries to pass as parameters
1207 //need to move this logic to WoocommerceService::updateProductShippingInfo()
1208 $product_country_from = !empty($importedProduct[ImportedProductService::FIELD_COUNTRY_FROM]) ?
1209 $importedProduct[ImportedProductService::FIELD_COUNTRY_FROM] :
1210 'CN';
1211
1212 $product_country_to = !empty($importedProduct[ImportedProductService::FIELD_COUNTRY_TO])
1213 ? $importedProduct[ImportedProductService::FIELD_COUNTRY_TO]
1214 : '';
1215
1216 $countryToCode = $_POST['country_to'] ?? $product_country_to;
1217 // $countryFromCode = !empty($_POST['country_from']) ? $_POST['country_from'] : $product_country_from;
1218
1219 $countryFromCode = $this->WoocommerceService->getShippingFromByProduct($WC_ProductOrVariation);
1220
1221 try {
1222 $importedProduct = $this->WoocommerceService->updateProductShippingInfo(
1223 $WC_ProductOrVariation, $countryToCode,
1224 );
1225
1226 $shippingItems = $this->ProductService->getShippingItems(
1227 $importedProduct, $countryToCode, $countryFromCode
1228 );
1229 } catch (RepositoryException $RepositoryException) {
1230 a2wl_error_log($RepositoryException->getMessage());
1231
1232 echo wp_json_encode(ResultBuilder::buildError(
1233 'Can`t get product shipping')
1234 );
1235
1236 wp_die();
1237 } catch (ServiceException $ServiceException) {
1238 $shippingItems = [];
1239
1240 $response = ResultBuilder::buildError($ServiceException->getMessage());
1241 echo wp_json_encode($response);
1242 wp_die();
1243 }
1244
1245 $product = $this->PriceFormulaService->applyFormula($importedProduct);
1246
1247 $variationList = [];
1248 if (isset($importedProduct['sku_products']['variations'])) {
1249 foreach ($importedProduct['sku_products']['variations'] as $variation) {
1250 $variationList[] = [
1251 'id' => $variation[ImportedProductService::FIELD_EXTERNAL_SKU_ID],
1252 'calc_price' => $variation['calc_price'],
1253 'calc_regular_price' => $variation['calc_regular_price'],
1254 'title' => $variation['title'],
1255 'ship_from' => $variation[ImportedProductService::FIELD_COUNTRY_CODE],
1256 ];
1257 }
1258 }
1259
1260 $result[] = [
1261 'product_id' => $wcProductId,
1262 'default_method' => $importedProduct[ImportedProductService::FIELD_METHOD] ?? '',
1263 'items' => $shippingItems,
1264 'shipping_cost' => $product[ImportedProductService::FIELD_COST] ?? '',
1265 'variations' => $variationList
1266 ];
1267
1268 echo wp_json_encode(ResultBuilder::buildOk(['products' => $result]));
1269 wp_die();
1270 }
1271
1272 /**
1273 * Load shipping info in the ImportList
1274 * @return void
1275 */
1276 private function ajax_load_shipping_info_for_import(): void
1277 {
1278 $externalSkuId = $_POST['variation_key'] ? sanitize_text_field($_POST['variation_key']) : null;
1279 $externalProductId = $_POST['id'] ? sanitize_text_field($_POST['id']) : null;
1280
1281 try {
1282 $importedProduct = $this->ProductImportModel->getProduct($externalProductId);
1283 } catch (ServiceException $Exception) {
1284 $response = ResultBuilder::buildError($Exception->getMessage());
1285 echo wp_json_encode($response);
1286 wp_die();
1287 }
1288
1289 //todo: Here we take product countries to pass as parameters
1290 //need to move this logic to WoocommerceService::updateProductShippingInfo()
1291 $product_country_from = !empty($importedProduct[ImportedProductService::FIELD_COUNTRY_FROM]) ?
1292 $importedProduct[ImportedProductService::FIELD_COUNTRY_FROM] :
1293 'CN';
1294
1295 $product_country_to = !empty($importedProduct[ImportedProductService::FIELD_COUNTRY_TO])
1296 ? $importedProduct[ImportedProductService::FIELD_COUNTRY_TO]
1297 : '';
1298
1299 $countryToCode = $_POST['country_to'] ?? $product_country_to;
1300 $countryFromCode = $this->ProductService->getShippingFromByExternalSkuId(
1301 $importedProduct, $externalSkuId
1302 );
1303
1304 $countryCode = ProductShippingData::meta_key($countryFromCode, $countryToCode);
1305 //need fresh shipping items data on the fulfillment page, clean cache
1306 $importedProduct[ImportedProductService::FIELD_SHIPPING_INFO][$countryCode] = [];
1307
1308 try {
1309 $importedProduct = $this->ProductService->updateProductShippingInfo(
1310 $importedProduct, $countryFromCode,
1311 $countryToCode, $externalSkuId, null
1312 );
1313
1314 $shippingItems = $this->ProductService->getShippingItems(
1315 $importedProduct, $countryToCode, $countryFromCode
1316 );
1317 } catch (ServiceException $ServiceException) {
1318 $shippingItems = [];
1319 $response = ResultBuilder::buildError($ServiceException->getMessage());
1320 echo wp_json_encode($response);
1321 wp_die();
1322 }
1323
1324 $importedProduct = $this->PriceFormulaService->applyFormula($importedProduct);
1325
1326 $variationList = [];
1327 if (isset($importedProduct['sku_products']['variations'])) {
1328 foreach ($importedProduct['sku_products']['variations'] as $variation) {
1329 $shipFrom = $variation[ImportedProductService::FIELD_COUNTRY_CODE] ?? 'CN';
1330 $title = str_replace($shipFrom, '', implode(" ", $variation['attributes_names']));
1331 $variationList[] = [
1332 'id' => $variation[ImportedProductService::FIELD_EXTERNAL_SKU_ID],
1333 'calc_price' => $variation['calc_price'],
1334 'calc_regular_price' => $variation['calc_regular_price'],
1335 'title' => $title,
1336 'ship_from' => $shipFrom,
1337 ];
1338 }
1339 }
1340 //todo: loaded shipping info is not saved here, need to fix that
1341 $this->ProductImportModel->upd_product($importedProduct);
1342
1343 $result[] = [
1344 'product_id' => $externalProductId,
1345 'default_method' => $importedProduct[ImportedProductService::FIELD_METHOD] ?? '',
1346 'items' => $shippingItems,
1347 'shipping_cost' => $product[ImportedProductService::FIELD_COST] ?? '',
1348 'variations' => $variationList,
1349 'variation_key' => $externalSkuId,
1350 ];
1351
1352 echo wp_json_encode(ResultBuilder::buildOk(['products' => $result]));
1353 wp_die();
1354 }
1355
1356 public function ajax_set_shipping_info(): void
1357 {
1358 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
1359
1360 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
1361 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
1362 echo wp_json_encode($result);
1363 wp_die();
1364 }
1365
1366 if (isset($_POST['id'])) {
1367 $product = $this->ProductImportModel->get_product($_POST['id']);
1368
1369 if ($product) {
1370 $product = $this->ProductService->setShippingInfo(
1371 $product,
1372 $_POST['method'] ?? '',
1373 $_POST['variation_key'] ?? null,
1374 $_POST['country_to'] ?? null,
1375 $_POST['country_from'] ?? null
1376 );
1377
1378 $this->ProductImportModel->upd_product($product);
1379
1380 $variationList = [];
1381 foreach ($product['sku_products']['variations'] as $variation) {
1382 $variationList[] = [
1383 'id' => $variation[ImportedProductService::FIELD_EXTERNAL_SKU_ID],
1384 'calc_price' => $variation['calc_price'],
1385 'calc_regular_price' => $variation['calc_regular_price']
1386 ];
1387 }
1388
1389 $result = [
1390 'default_method' => $product[ImportedProductService::FIELD_METHOD],
1391 'shipping_cost' => $product[ImportedProductService::FIELD_COST],
1392 'variations' => $variationList
1393 ];
1394 echo wp_json_encode(ResultBuilder::buildOk($result));
1395 } else {
1396 echo wp_json_encode(ResultBuilder::buildError("Product not found."));
1397 }
1398 } else {
1399 echo wp_json_encode(ResultBuilder::buildError("set_shipping_info: waiting for ID..."));
1400 }
1401
1402 wp_die();
1403 }
1404
1405 public function ajax_update_shipping_list(): void
1406 {
1407 check_admin_referer(self::AJAX_NONCE_ACTION, self::NONCE);
1408
1409 if (!PageGuardHelper::canAccessPage(Pages::IMPORT_LIST)) {
1410 $result = ResultBuilder::buildError($this->getErrorTextNoPermissions());
1411 echo wp_json_encode($result);
1412 wp_die();
1413 }
1414
1415
1416 echo wp_json_encode(ResultBuilder::buildOk());
1417 wp_die();
1418 }
1419
1420 /**
1421 * Prepare system for heavy non-background import task
1422 * @todo move this method to Woocommerce model
1423 * @return void
1424 */
1425 private function prepareSystemForImport(): void
1426 {
1427 ini_set("memory_limit", -1);
1428 set_time_limit(0);
1429 ignore_user_abort(true);
1430
1431 if (!a2wl_check_defined('A2WL_DO_NOT_USE_TRANSACTION')) {
1432 global $wpdb;
1433
1434 wp_defer_term_counting(true);
1435 wp_defer_comment_counting(true);
1436 $wpdb->query('SET autocommit = 0;');
1437
1438 register_shutdown_function(function () {
1439 global $wpdb;
1440 $wpdb->query('COMMIT;');
1441 //we use @ to prevent errors because during testing, terms are removed already on shutdown
1442 @wp_defer_term_counting(false);
1443 wp_defer_comment_counting(false);
1444 });
1445 }
1446 }
1447
1448 private function respondWithError(string $message): void
1449 {
1450 echo wp_json_encode(ResultBuilder::buildError($message));
1451 wp_die();
1452 }
1453 }
1454