PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.21
ووسلام – همگام سازی ووکامرس و باسلام v1.10.21
1.10.21 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 All 54 releases
← All changes | includes/Services/Products/UpdateProductVariationsService.php +68 -9 1.10.171.10.21 View file →
@@ -4,8 +4,9 @@
4 4
5 5 use SyncBasalam\Config\Endpoints;
6 6 use SyncBasalam\Jobs\Exceptions\NonRetryableException;
7 7 use SyncBasalam\Jobs\Exceptions\RetryableException;
8 +use SyncBasalam\Jobs\Exceptions\StaleVariationException;
8 9 use SyncBasalam\Logger\Logger;
9 10 use SyncBasalam\Services\ApiServiceManager;
10 11
11 12 defined('ABSPATH') || exit;
@@ -16,11 +17,11 @@
16 17 private const VARIATION_FIELDS = ['primary_price', 'stock', 'sku'];
17 18
18 19 private $apiservice;
19 20
20 - public function __construct()
21 + public function __construct($apiservice = null)
21 22 {
22 - $this->apiservice = syncBasalamContainer()->get(ApiServiceManager::class);
23 + $this->apiservice = $apiservice ?: syncBasalamContainer()->get(ApiServiceManager::class);
23 24 }
24 25
25 26 /**
26 27 * True when every variant already has a Basalam variation id, so each one can be
@@ -60,22 +61,30 @@
60 61
61 62 $url = sprintf(Endpoints::PRODUCT_VARIATION_UPDATE, $basalamProductId, $variant['id']);
62 63
63 64 try {
64 - $this->apiservice->patch($url, $data);
65 + $this->sendVariationUpdate($url, $data);
65 66 $updated++;
66 67 } catch (RetryableException $e) {
67 68 throw $e;
69 + } catch (NonRetryableException $e) {
70 + // A missing remote variation means the local mapping is stale,
71 + // not that the whole product is missing. Abort this per-variant
72 + // pass immediately so the caller can rebuild all mappings in a
73 + // single product update.
74 + if ((int) $e->getCode() === 404) {
75 + throw new StaleVariationException($basalamProductId, $variant['id'], $e);
76 + }
77 +
78 + $failed++;
79 + $firstError = $firstError ?: $e->getMessage();
80 +
81 + $this->logVariationError($e, $productId, $basalamProductId, $variant['id']);
68 82 } catch (\Exception $e) {
69 83 $failed++;
70 84 $firstError = $firstError ?: $e->getMessage();
71 85
72 - Logger::error('خطا در بروزرسانی متغیر محصول در باسلام: ' . $e->getMessage(), [
73 - 'operation' => 'بروزرسانی متغیر محصول',
74 - 'product_id' => $productId,
75 - 'basalam_product_id' => $basalamProductId,
76 - 'basalam_variation_id' => $variant['id'],
77 - ]);
86 + $this->logVariationError($e, $productId, $basalamProductId, $variant['id']);
78 87 }
79 88 }
80 89
81 90 // Every variation failed with a permanent error: report it instead of pretending success.
@@ -83,6 +92,56 @@
83 92 throw NonRetryableException::permanent(esc_html('بروزرسانی متغیرهای محصول ناموفق بود: ' . $firstError));
84 93 }
85 94
86 95 return ['updated' => $updated, 'skipped' => $skipped, 'failed' => $failed];
96 + }
97 +
98 + private function logVariationError(\Throwable $error, $productId, $basalamProductId, $basalamVariationId): void
99 + {
100 + Logger::error('خطا در بروزرسانی متغیر محصول در باسلام: ' . $error->getMessage(), [
101 + 'operation' => 'بروزرسانی متغیر محصول',
102 + 'product_id' => $productId,
103 + 'basalam_product_id' => $basalamProductId,
104 + 'basalam_variation_id' => $basalamVariationId,
105 + ]);
106 + }
107 +
108 + private function sendVariationUpdate(string $url, array $data): void
109 + {
110 + $skuRetry = false;
111 +
112 + while (true) {
113 + try {
114 + $response = $this->apiservice->patch($url, $data);
115 + } catch (RetryableException $e) {
116 + if ($this->retryWithoutDuplicateSku($data, $e, $skuRetry)) continue;
117 +
118 + throw $e;
119 + } catch (\Exception $e) {
120 + if ($this->retryWithoutDuplicateSku($data, $e, $skuRetry)) continue;
121 +
122 + throw $e;
123 + }
124 +
125 + if ($this->retryWithoutDuplicateSku($data, $response, $skuRetry)) continue;
126 +
127 + // API adapters normally throw client errors, but keep the fallback
128 + // bounded if an adapter returns the error response directly.
129 + if (ProductSkuRetry::isDuplicateSkuError($response)) {
130 + throw NonRetryableException::permanent('SKU متغیر محصول در باسلام تکراری است.');
131 + }
132 +
133 + return;
134 + }
135 + }
136 +
137 + private function retryWithoutDuplicateSku(array &$data, $error, bool &$retried): bool
138 + {
139 + if ($retried || !ProductSkuRetry::hasSku($data)) return false;
140 + if (!ProductSkuRetry::isDuplicateSkuError($error)) return false;
141 +
142 + $data = ProductSkuRetry::withoutSkus($data);
143 + $retried = true;
144 +
145 + return true;
87 146 }
88 147 }