PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.8.6
ووسلام – همگام سازی ووکامرس و باسلام v1.8.6
1.10.19 1.10.20 1.10.18 1.10.17 1.10.15 1.10.14 1.10.13 1.10.12 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.2 1.9.1 1.9.0 1.8.8 1.8.5 1.8.6 All 53 releases
sync-basalam / includes / Services / Orders / OrderManager.php

OrderManager.php in ووسلام – همگام سازی ووکامرس و باسلام 1.8.6, at includes/Services/Orders/OrderManager.php

789 lines 30.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SyncBasalam\Services\Orders;
4
5 use SyncBasalam\Admin\Settings\SettingsConfig;
6 use SyncBasalam\Config\Endpoints;
7 use SyncBasalam\Logger\Logger;
8 use SyncBasalam\Utilities\GetProvincesData;
9 use SyncBasalam\Utilities\ProductMetaKey;
10 use SyncBasalam\Services\ApiServiceManager;
11
12 defined('ABSPATH') || exit;
13
14 class OrderManager
15 {
16
17 private static function shouldBypassWccfOrderHooks()
18 {
19 return class_exists('WCCF_WC_Order') && !class_exists('RightPress_Product_Price_Shop');
20 }
21
22 private static function removeWccfOrderSaveHooks()
23 {
24 global $wp_filter;
25
26 $removedHooks = [];
27
28 if (!is_array($wp_filter) && !($wp_filter instanceof \ArrayAccess)) {
29 return $removedHooks;
30 }
31
32 foreach ($wp_filter as $hookName => $hook) {
33 if (!isset($hook->callbacks) || !is_array($hook->callbacks)) {
34 continue;
35 }
36
37 foreach ($hook->callbacks as $priority => $callbacks) {
38 foreach ($callbacks as $callbackConfig) {
39 $callback = $callbackConfig['function'] ?? null;
40
41 if (!is_array($callback) || !is_object($callback[0] ?? null) || !isset($callback[1])) {
42 continue;
43 }
44
45 if (!($callback[0] instanceof \WCCF_WC_Order) || $callback[1] !== 'save_order_field_values') {
46 continue;
47 }
48
49 if (remove_filter($hookName, $callback, $priority)) {
50 $removedHooks[] = [
51 'hook' => $hookName,
52 'callback' => $callback,
53 'priority' => $priority,
54 'accepted_args' => $callbackConfig['accepted_args'] ?? 1,
55 ];
56 }
57 }
58 }
59 }
60
61 return $removedHooks;
62 }
63
64 private static function restoreWccfOrderSaveHooks(array $removedHooks)
65 {
66 foreach ($removedHooks as $hookConfig) {
67 add_filter(
68 $hookConfig['hook'],
69 $hookConfig['callback'],
70 $hookConfig['priority'],
71 $hookConfig['accepted_args']
72 );
73 }
74 }
75
76 private static function executeWithoutWccfOrderSaveHooks(callable $callback, $invoice_id, $contextLabel)
77 {
78 $removedWccfHooks = [];
79
80 if (self::shouldBypassWccfOrderHooks()) {
81 $removedWccfHooks = self::removeWccfOrderSaveHooks();
82 }
83
84 try {
85 return $callback();
86 } finally {
87 if (!empty($removedWccfHooks)) {
88 self::restoreWccfOrderSaveHooks($removedWccfHooks);
89 }
90 }
91 }
92
93 public static function createOrderWooFromRequest(\WP_REST_Request $request)
94 {
95 return self::createRestResponse(
96 self::createOrderWoo($request->get_params())
97 );
98 }
99
100 public static function orderManger(\WP_REST_Request $request, $checkSyncStatus = true)
101 {
102 $parsedParams = $request->get_params();
103
104 if ($checkSyncStatus && !syncBasalamSettings()->getSettings(SettingsConfig::SYNC_STATUS_ORDER)) {
105 return self::createRestResponse([
106 'success' => true,
107 'message' => 'Order sync is disabled.',
108 'status' => 200,
109 ]);
110 }
111
112 Logger::debug("دریافت رویداد سفارش: " . json_encode($parsedParams));
113
114 if (isset($parsedParams['event_id']) && $parsedParams['event_id'] == 7) {
115 if ($parsedParams['type'] == 'shipped') {
116 return self::createRestResponse(self::shippedOrderWoo($parsedParams['invoice_id']));
117 } elseif ($parsedParams['type'] == 'cancelled') {
118 return self::createRestResponse(self::cancelOrderWoo($parsedParams['invoice_id']));
119 } elseif ($parsedParams['type'] == 'preparation') {
120 return self::createRestResponse(self::confirmOrderWoo($parsedParams['invoice_id']));
121 }
122 } elseif (isset($parsedParams['event_id']) && $parsedParams['event_id'] == 3) {
123 if ($parsedParams['status'] == '3195') {
124 return self::createRestResponse(self::completeOrderWoo($parsedParams['more_data']['invoice_id']));
125 } elseif ($parsedParams['status'] == '3067' || $parsedParams['status'] == '3233') {
126 return self::createRestResponse(self::cancelOrderWoo($parsedParams['more_data']['invoice_id']));
127 }
128 } else {
129 return self::createOrderWooFromRequest($request);
130 }
131
132 return self::createRestResponse([
133 'success' => false,
134 'message' => 'Unsupported order event.',
135 'error' => 'The incoming event did not match any supported order action.',
136 'status' => 400,
137 ]);
138 }
139
140 public static function createOrderWoo($params)
141 {
142 $payment_id = $params['payment_id'] ?? null;
143 $invoice_id = $params['invoice_id'] ?? null;
144 $user_id = $params['user_id'] ?? null;
145 $city_id = $params['city_id'] ?? null;
146 $province_id = $params['province_id'] ?? null;
147
148 global $wpdb;
149 $table_name = $wpdb->prefix . 'sync_basalam_payments';
150
151 if (empty($invoice_id)) {
152 return [
153 'success' => false,
154 'message' => 'Missing invoice_id.',
155 'error' => 'invoice_id is required to create an order.',
156 'status' => 400,
157 ];
158 }
159
160 $existingOrderId = $wpdb->get_var(
161 $wpdb->prepare(
162 "SELECT order_id FROM {$table_name} WHERE invoice_id = %d LIMIT 1",
163 $invoice_id
164 )
165 );
166
167 if ($existingOrderId) {
168 return [
169 'success' => true,
170 'message' => 'Order already exists.',
171 'order_id' => (int) $existingOrderId,
172 'status' => 200,
173 ];
174 }
175
176 $lockName = 'sync_basalam_invoice_' . $invoice_id;
177 $gotLock = $wpdb->get_var(
178 $wpdb->prepare("SELECT GET_LOCK(%s, 0)", $lockName)
179 );
180
181 if ($gotLock !== '1' && $gotLock !== 1) {
182 Logger::debug("ریکوئست تکراری webhook برای invoice_id {$invoice_id} ـ نادیده گرفته شد (در حال پردازش توسط ریکوئست دیگر).");
183 return [
184 'success' => true,
185 'message' => 'Order is already being processed by another request.',
186 'status' => 200,
187 ];
188 }
189
190 try {
191 $existingOrderId = $wpdb->get_var(
192 $wpdb->prepare(
193 "SELECT order_id FROM {$table_name} WHERE invoice_id = %d LIMIT 1",
194 $invoice_id
195 )
196 );
197
198 if ($existingOrderId) {
199 return [
200 'success' => true,
201 'message' => 'Order already exists.',
202 'order_id' => (int) $existingOrderId,
203 'status' => 200,
204 ];
205 }
206
207 return self::createOrderWooLocked($params, $invoice_id, $payment_id, $user_id, $city_id, $province_id, $table_name);
208 } finally {
209 $wpdb->query(
210 $wpdb->prepare("SELECT RELEASE_LOCK(%s)", $lockName)
211 );
212 }
213 }
214
215 private static function createOrderWooLocked($params, $invoice_id, $payment_id, $user_id, $city_id, $province_id, $table_name)
216 {
217 global $wpdb;
218
219 $wpdb->query('START TRANSACTION');
220 try {
221
222 $vendor_id = syncBasalamSettings()->getSettings(SettingsConfig::VENDOR_ID);
223
224 $api_url = sprintf(Endpoints::ORDER_DETAIL, $vendor_id, $invoice_id);
225
226 $apiServiceManager = syncBasalamContainer()->get(ApiServiceManager::class);
227
228 $response = $apiServiceManager->get($api_url);
229
230 if (isset($response['success']) && !$response['success']) {
231 $wpdb->query('ROLLBACK');
232 Logger::error("درخواست API نا�
233 وفق بود: " . ($response['error'] ?? 'خطای نا�
234 شخص'));
235
236 return [
237 'success' => false,
238 'message' => 'Failed to fetch invoice details.',
239 'error' => $response['error'] ?? 'Unknown error',
240 'status' => 500,
241 ];
242 }
243
244 $api_response = $response['body'] ?? '';
245 $data = json_decode($api_response, true);
246
247 if (json_last_error() !== JSON_ERROR_NONE) {
248 $wpdb->query('ROLLBACK');
249
250 return [
251 'success' => false,
252 'message' => 'Failed to parse API response.',
253 'error' => 'Invalid JSON response: ' . json_last_error_msg(),
254 'status' => 500,
255 ];
256 }
257
258 if (empty($data)) {
259 $wpdb->query('ROLLBACK');
260 Logger::error("پاسخ خالی از API برای فاکتور دریافت شد: $invoice_id");
261
262 return [
263 'success' => false,
264 'message' => 'Empty API response.',
265 'error' => 'No data received from API',
266 'status' => 500,
267 ];
268 }
269
270 $user_id = $user_id ?? ($data['customer_data']['user']['id'] ?? null);
271 $city_id = $city_id ?? ($data['customer_data']['city']['id'] ?? null);
272 $province_id = $province_id ?? ($data['customer_data']['city']['parent']['id'] ?? null);
273
274 $order = self::executeWithoutWccfOrderSaveHooks(function () {
275 return wc_create_order();
276 }, $invoice_id, 'wc_create_order');
277
278 if (is_wp_error($order)) {
279 throw new \RuntimeException('wc_create_order failed: ' . $order->get_error_message());
280 }
281
282 if (!$order instanceof \WC_Order) {
283 throw new \RuntimeException('wc_create_order did not return a valid WC_Order instance.');
284 }
285 if (isset($data['items']) && is_array($data['items'])) {
286 foreach ($data['items'] as $item) {
287 $sync_basalam_product_id = $item['product']['id'] ?? null;
288 $quantity = $item['quantity'] ?? 1;
289 $item_id = $item['id'] ?? null;
290
291 if ($sync_basalam_product_id) {
292 try {
293 if (!empty($item['variation']['id'])) {
294 $woo_product_id = self::getWooProductVariableId($item['variation']['id']);
295 } else {
296 $woo_product_id = self::getWooProductSimpleId($sync_basalam_product_id);
297 }
298
299 if ($woo_product_id) {
300 $product = wc_get_product($woo_product_id);
301 if ($product) {
302 $order_item_id = $order->add_product($product, $quantity);
303 if ($item_id && $order_item_id) {
304 $order->update_meta_data('_sync_basalam_item_id_' . $order_item_id, $item_id);
305 }
306
307 self::set_item_price_from_financial_report($order, $order_item_id, $item, $quantity);
308 }
309 } else {
310 $placeholder_product_id = self::getPlaceholderProductId();
311 if ($placeholder_product_id) {
312 $placeholder_product = wc_get_product($placeholder_product_id);
313 if ($placeholder_product) {
314 $order_item_id = $order->add_product($placeholder_product, $quantity);
315
316 if ($item_id && $order_item_id) {
317 $order->update_meta_data('_sync_basalam_item_id_' . $order_item_id, $item_id);
318 }
319
320 self::set_item_price_from_financial_report($order, $order_item_id, $item, $quantity);
321 }
322 }
323 }
324 } catch (\Exception $e) {
325 Logger::error('خطا در ایجاد سفارش: ' . $e->getMessage());
326 }
327 }
328 }
329 }
330
331 if (isset($data['customer_data']['recipient']) && is_array($data['customer_data']['recipient'])) {
332 $recipient = $data['customer_data']['recipient'];
333 $province = $data['customer_data']['city']['parent']['title'] ?? '';
334 $city = $data['customer_data']['city']['title'] ?? '';
335
336 $full_name = $recipient['name'] ?? '';
337 $first_name = '';
338 $last_name = '';
339 if (!empty($full_name)) {
340 $parts = explode(' ', trim($full_name));
341 $parts = array_filter($parts);
342
343 if (count($parts) === 1) {
344 $first_name = $parts[0];
345 $last_name = $parts[0];
346 } else {
347 $first_name = array_shift($parts);
348 $last_name = implode(' ', $parts);
349 }
350 }
351
352 $prefix = syncBasalamSettings()->getSettings(SettingsConfig::CUSTOMER_PREFIX_NAME);
353 $suffix = syncBasalamSettings()->getSettings(SettingsConfig::CUSTOMER_SUFFIX_NAME);
354
355 if (!empty($prefix)) $first_name = $prefix . ' ' . $first_name;
356 if (!empty($suffix)) $last_name = $last_name . ' ' . $suffix;
357
358 // Set basic billing info
359 $order->set_billing_first_name($first_name);
360 $order->set_billing_last_name($last_name);
361 $order->set_billing_address_1($recipient['postal_address'] ?? '');
362 $order->set_billing_postcode($recipient['postal_code'] ?? '');
363 $order->set_billing_country('IR');
364 $order->set_billing_phone($recipient['mobile'] ?? '');
365
366 // Set basic shipping info
367 $order->set_shipping_first_name($first_name);
368 $order->set_shipping_last_name($last_name);
369 $order->set_shipping_address_1($recipient['postal_address'] ?? '');
370 $order->set_shipping_postcode($recipient['postal_code'] ?? '');
371 $order->set_shipping_phone($recipient['mobile'] ?? '');
372 $order->set_shipping_country('IR');
373
374 // Set state and city with PWS compatibility
375 $addressData = [
376 'province' => $province,
377 'city' => $city,
378 ];
379 GetProvincesData::setOrderAddress($order, $addressData, 'billing');
380 GetProvincesData::setOrderAddress($order, $addressData, 'shipping');
381
382 // Add shipping method based on settings
383 $shipping_method_setting = syncBasalamSettings()->getSettings(SettingsConfig::ORDER_SHIPPING_METHOD);
384
385 if (isset($data['parcel_detail']['shipping_cost'])) {
386 $shipping_cost = $data['parcel_detail']['shipping_cost'];
387
388 $currency = get_woocommerce_currency();
389 if ($currency === 'IRT') {
390 $shipping_cost = $shipping_cost / 10;
391 } elseif ($currency === 'IRHT') {
392 $shipping_cost = $shipping_cost / 10000;
393 } elseif ($currency === 'IRHR') {
394 $shipping_cost = $shipping_cost / 1000;
395 }
396
397 $shipping_item = new \WC_Order_Item_Shipping();
398
399 if ($shipping_method_setting === 'basalam') {
400 // Use Basalam shipping method title from API
401 if (isset($data['parcel_detail']['shipping_method']['title'])) {
402 $shipping_method_title = $data['parcel_detail']['shipping_method']['title'];
403 $shipping_item->set_method_title($shipping_method_title);
404 }
405 $shipping_item->set_method_id('basalam_shipping');
406 } elseif (strpos($shipping_method_setting, 'wc_') === 0) {
407 // Use WooCommerce shipping method
408 $wc_method_id = substr($shipping_method_setting, 3); // Remove 'wc_' prefix
409
410 // Find the shipping method instance
411 $method_instance_id = self::findShippingMethodInstanceId($wc_method_id);
412 if ($method_instance_id) {
413 $shipping_item->set_method_id($wc_method_id . ':' . $method_instance_id);
414
415 // Get the method title from WooCommerce
416 $method_title = self::getShippingMethodTitle($wc_method_id, $method_instance_id);
417 if ($method_title) {
418 $shipping_item->set_method_title($method_title);
419 }
420 } else {
421 // Fallback to method id without instance
422 $shipping_item->set_method_id($wc_method_id);
423 $shipping_item->set_method_title($wc_method_id);
424 }
425 }
426
427 $shipping_item->set_total(floatval($shipping_cost));
428 $shipping_item->set_taxes([]);
429 $order->add_item($shipping_item);
430 }
431 }
432
433 $order->calculate_totals();
434
435 $total_price = 0;
436 $products_total = 0;
437
438 if (isset($data['items']) && is_array($data['items'])) {
439 foreach ($data['items'] as $item) {
440 if (isset($item['financial_report']['report_items']) && is_array($item['financial_report']['report_items'])) {
441 foreach ($item['financial_report']['report_items'] as $report_item) {
442 if (isset($report_item['title']) && $report_item['title'] === 'قی�
443 ت �
444 حصول' && isset($report_item['amount'])) {
445 $products_total += (int) $report_item['amount'];
446 break;
447 }
448 }
449 }
450 }
451 }
452
453 if ($products_total > 0) {
454 $total_price = $products_total;
455 } else {
456 if (isset($data['financial_report']['product_cost']['report_items'][0]['amount'])) {
457 $total_price += $data['financial_report']['product_cost']['report_items'][0]['amount'];
458 }
459 }
460
461 if (isset($data['financial_report']['shipping_cost']['total']['amount'])) {
462 $total_price += $data['financial_report']['shipping_cost']['total']['amount'];
463 }
464
465 if ($total_price > 0) {
466 $currency = get_woocommerce_currency();
467 if ($currency === 'IRT') {
468 $total_price = $total_price / 10;
469 } elseif ($currency === 'IRHT') {
470 $total_price = $total_price / 10000;
471 } elseif ($currency === 'IRHR') {
472 $total_price = $total_price / 1000;
473 }
474 $order->set_total($total_price);
475 }
476
477 $order->set_payment_method('basalam payment method');
478 $order->set_payment_method_title('Basalam Payment');
479
480 $orderStatusType = syncBasalamSettings()->getSettings(SettingsConfig::ORDER_STATUES_TYPE);
481
482 $status_map = [
483 3067 => 'bslm-rejected',
484 3739 => 'bslm-wait-vendor',
485 3237 => 'bslm-preparation',
486 3238 => 'bslm-shipping',
487 3195 => 'bslm-completed',
488 3233 => 'bslm-rejected',
489 ];
490 $status_id = $data['status']['id'] ?? null;
491
492 if ($orderStatusType == 'woocommerce_statuses') {
493 $order->set_status('processing');
494 } else {
495 $order_status = $status_map[$status_id] ?? 'bslm-wait-vendor';
496 $order->set_status($order_status);
497 }
498
499 $purchase_count = $data['customer_data']['purchase_count'];
500 $fee_amount = $data['financial_report']['product_cost']['report_items'][2]['amount'] ?? 0;
501 $balance_amount = $data['financial_report']['product_cost']['total']['amount'] ?? 0;
502
503
504 $order->update_meta_data('_basalam_fee_amount', intval($fee_amount / 10));
505 $order->update_meta_data('_basalam_balance_amount', intval($balance_amount / 10));
506 $order->update_meta_data('_basalam_purchase_count', $purchase_count);
507
508 if (isset($data['hash_id'])) {
509 $order->update_meta_data('_sync_basalam_hash_id', $data['hash_id']);
510 }
511
512 self::executeWithoutWccfOrderSaveHooks(function () use ($order) {
513 $order->save();
514 }, $invoice_id, 'order->save()');
515
516 $order_id = $order->get_id();
517 if ($order_id) {
518
519 $insert_result = $wpdb->insert(
520 $table_name,
521 [
522 'payment_id' => $payment_id,
523 'invoice_id' => $invoice_id,
524 'user_id' => $user_id,
525 'city_id' => $city_id,
526 'province_id' => $province_id,
527 'order_id' => $order_id,
528 ],
529 ['%d', '%d', '%d', '%d', '%d', '%d']
530 );
531
532 if ($insert_result === false) {
533 throw new \Exception("خطا در ذخیره اطلاعات سفارش در جدول sync_basalam_payments");
534 }
535
536 update_post_meta($order_id, '_is_sync_basalam_order', true);
537
538 $wpdb->query('COMMIT');
539
540 return [
541 'success' => true,
542 'message' => 'Order created successfully',
543 'order_id' => $order_id,
544 'status' => 200,
545 ];
546 } else {
547 throw new \Exception("خطا در ایجاد سفارش با شناسه $invoice_id ، از گزینه بررسی سفارشات استفاده ن�
548 ایید.");
549 }
550 } catch (\Exception $e) {
551 $wpdb->query('ROLLBACK');
552
553 Logger::error($e->getMessage());
554
555 return [
556 'success' => false,
557 'message' => 'Failed to create order.',
558 'error' => $e->getMessage(),
559 'status' => 500,
560 ];
561 }
562 }
563
564 public static function cancelOrderWoo($invoice_id)
565 {
566 return self::updateOrderStatus($invoice_id, 'bslm-rejected', 'bslm-rejected');
567 }
568
569 public static function completeOrderWoo($invoice_id)
570 {
571 return self::updateOrderStatus($invoice_id, 'bslm-completed', 'bslm-completed');
572 }
573
574 public static function confirmOrderWoo($invoice_id)
575 {
576 return self::updateOrderStatus($invoice_id, 'bslm-preparation', 'bslm-preparation');
577 }
578
579 public static function shippedOrderWoo($invoice_id)
580 {
581 return self::updateOrderStatus($invoice_id, 'bslm-shipping', 'bslm-shipping');
582 }
583
584 public static function updateOrderStatus($invoice_id, $status, $job = null)
585 {
586 global $wpdb;
587 $table_name = $wpdb->prefix . 'sync_basalam_payments';
588
589 $order_id = $wpdb->get_var(
590 $wpdb->prepare("SELECT order_id FROM {$table_name} WHERE invoice_id = %d", $invoice_id)
591 );
592
593 if (!$order_id) {
594 $create_result = self::createOrderWoo([
595 'invoice_id' => $invoice_id,
596 ]);
597
598 if (!empty($create_result['success']) && !empty($create_result['order_id'])) {
599 $order_id = $create_result['order_id'];
600 }
601 }
602
603 if (!$order_id) {
604 return [
605 'success' => false,
606 'message' => 'Order not found.',
607 'error' => "No WooCommerce order found for invoice_id {$invoice_id}.",
608 'invoice_id' => $invoice_id,
609 'status' => 404,
610 ];
611 }
612
613 $order = wc_get_order($order_id);
614
615 if ($order && $order instanceof \WC_Order) {
616 $order->update_status($status);
617 return [
618 'success' => true,
619 'message' => 'Order status updated successfully.',
620 'order_id' => $order_id,
621 'invoice_id' => $invoice_id,
622 'job' => $job,
623 'status_key' => $status,
624 'status' => 200,
625 ];
626 }
627
628 self::logError("آبجکت سفارش ووکا�
629 رس برای order_id {$order_id} و invoice_id {$invoice_id} �
630 عتبر نیست");
631 return [
632 'success' => false,
633 'message' => 'Invalid WooCommerce order object.',
634 'error' => "Invalid WooCommerce order object for order_id {$order_id} and invoice_id {$invoice_id}.",
635 'order_id' => $order_id,
636 'invoice_id' => $invoice_id,
637 'status' => 500,
638 ];
639 }
640
641 public static function getPlaceholderProductId()
642 {
643 $placeholder_name = 'این �
644 حصول در سایت ش�
645 ا تعریف نشده است ، برای �
646 شاهده جزییات به باسلا�
647
648 راجعه کنید';
649 $product_id = self::productExistsByTitle($placeholder_name);
650 if (!$product_id) {
651 $product = new \WC_Product_Simple();
652 $product->set_name($placeholder_name);
653 $product->set_status('draft');
654 $product->set_sku('placeholder-basalam-product');
655 $product->save();
656 $product_id = $product->get_id();
657 }
658
659 return $product_id;
660 }
661
662 public static function getWooProductSimpleId($sync_basalam_product_id)
663 {
664 $product = get_posts([
665 'post_type' => 'product',
666 'meta_key' => ProductMetaKey::basalamProductId(),
667 'meta_value' => $sync_basalam_product_id,
668 'posts_per_page' => 1,
669 ]);
670
671 return !empty($product) ? $product[0]->ID : null;
672 }
673
674 public static function getWooProductVariableId($sync_basalam_product_variant_id)
675 {
676 $args = [
677 'post_type' => 'product_variation',
678 'posts_per_page' => 1,
679 'meta_key' => 'sync_basalam_variation_id',
680 'meta_value' => $sync_basalam_product_variant_id,
681 'fields' => 'ids',
682 ];
683
684 $variation = get_posts($args);
685
686 return !empty($variation) ? $variation[0] : null;
687 }
688
689 public static function productExistsByTitle($title)
690 {
691 global $wpdb;
692 $product_id = $wpdb->get_var(
693 $wpdb->prepare(
694 "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'product' AND post_status != 'private' AND post_title = %s LIMIT 1",
695 $title
696 )
697 );
698
699 return $product_id ? $product_id : false;
700 }
701
702 private static function set_item_price_from_financial_report($order, $order_item_id, $item, $quantity)
703 {
704 $product_price = 0;
705 if (isset($item['financial_report']['report_items']) && is_array($item['financial_report']['report_items'])) {
706 foreach ($item['financial_report']['report_items'] as $report_item) {
707 if (isset($report_item['title']) && $report_item['title'] === 'قی�
708 ت �
709 حصول' && isset($report_item['amount'])) {
710 $product_price = (int) $report_item['amount'];
711 break;
712 }
713 }
714 }
715
716 if ($product_price > 0) {
717 $currency = get_woocommerce_currency();
718 if ($currency === 'IRT') {
719 $product_price = $product_price / 10;
720 } elseif ($currency === 'IRHT') {
721 $product_price = $product_price / 10000;
722 } elseif ($currency === 'IRHR') {
723 $product_price = $product_price / 1000;
724 }
725
726 $order_item = $order->get_item($order_item_id);
727 if ($order_item) {
728 $order_item->set_subtotal($product_price);
729 $order_item->set_total($product_price);
730 $order_item->save();
731 }
732 }
733 }
734
735 private static function findShippingMethodInstanceId($method_id)
736 {
737 if (!class_exists('WC_Shipping_Zones')) {
738 return null;
739 }
740
741 $shipping_zones = \WC_Shipping_Zones::get_zones();
742
743 foreach ($shipping_zones as $zone) {
744 $zone_id = $zone['id'] ?? 0;
745 $shipping_zone = new \WC_Shipping_Zone($zone_id);
746 $methods = $shipping_zone->get_shipping_methods(true);
747
748 foreach ($methods as $method) {
749 if ($method->id === $method_id) {
750 return $method->instance_id;
751 }
752 }
753 }
754
755 return null;
756 }
757
758 private static function getShippingMethodTitle($method_id, $instance_id)
759 {
760 if (!class_exists('WC_Shipping_Zones')) {
761 return null;
762 }
763
764 $shipping_zones = \WC_Shipping_Zones::get_zones();
765
766 foreach ($shipping_zones as $zone) {
767 $zone_id = $zone['id'] ?? 0;
768 $shipping_zone = new \WC_Shipping_Zone($zone_id);
769 $methods = $shipping_zone->get_shipping_methods(true);
770
771 foreach ($methods as $method) {
772 if ($method->id === $method_id && $method->instance_id == $instance_id) {
773 return $method->get_title() ?: $method->get_method_title();
774 }
775 }
776 }
777
778 return null;
779 }
780
781 private static function createRestResponse(array $result)
782 {
783 $status = (int) ($result['status'] ?? (!empty($result['success']) ? 200 : 500));
784 unset($result['status']);
785
786 return new \WP_REST_Response($result, $status);
787 }
788 }
789