PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.28
ووسلام – همگام سازی ووکامرس و باسلام v1.10.28
1.10.28 1.1.26 1.10.27 1.10.24 1.10.25 1.10.23 1.10.22 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 All 61 releases
sync-basalam / includes / Services / Products / Discount / DiscountTaskProcessor.php

DiscountTaskProcessor.php in ووسلام – همگام سازی ووکامرس و باسلام 1.10.28, at includes/Services/Products/Discount/DiscountTaskProcessor.php

326 lines 12.0 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\Products\Discount;
4
5 use SyncBasalam\JobManager;
6 use SyncBasalam\Admin\Settings\SettingsConfig;
7 use SyncBasalam\Services\Products\ProductConnection;
8 use SyncBasalam\Utilities\ProductMetaKey;
9
10 defined('ABSPATH') || exit;
11
12 class DiscountTaskProcessor
13 {
14 private $discountManager;
15 private $taskModel;
16 private $jobManager;
17 private $settingsAccessor;
18
19 public function __construct(
20 $discountManager = null,
21 $taskModel = null,
22 $jobManager = null
23 ) {
24 $this->discountManager = $discountManager ?: syncBasalamContainer()->get(DiscountManager::class);
25 $this->taskModel = $taskModel ?: syncBasalamContainer()->get(DiscountTaskModel::class);
26 $this->jobManager = $jobManager ?: syncBasalamContainer()->get(JobManager::class);
27 $this->settingsAccessor = syncBasalamSettings();
28 }
29
30 public function processDiscountTasks()
31 {
32 $job = $this->jobManager->getJob(['job_type' => 'sync_basalam_discount_tasks', 'status' => 'pending']);
33
34 if (!$job) return;
35
36 $this->jobManager->updateJob(['status' => 'processing', 'started_at' => time()], ['id' => $job->id]);
37
38 try {
39 $runnableTasks = $this->taskModel->getRunnableTasks();
40
41 if (!$runnableTasks) {
42 $this->jobManager->deleteJob(['id' => $job->id]);
43 return;
44 }
45
46 $this->processTaskGroup($runnableTasks);
47
48 $remainingTasks = $this->taskModel->getTasksCountByStatus(DiscountTaskModel::STATUS_PENDING);
49
50 if ($remainingTasks > 0) {
51 $this->jobManager->updateJob(
52 ['status' => 'pending', 'started_at' => 0],
53 ['id' => $job->id]
54 );
55 } else $this->jobManager->deleteJob(['id' => $job->id]);
56 } catch (\Exception $e) {
57 $this->jobManager->updateJob(
58 ['status' => 'failed', 'error_message' => $e->getMessage()],
59 ['id' => $job->id]
60 );
61 }
62 }
63
64 private function processTaskGroup($group)
65 {
66 $taskIds = !empty($group->task_ids) ? explode(',', $group->task_ids) : [];
67 $rawProductIds = !empty($group->product_ids) ? array_filter(explode(',', $group->product_ids)) : [];
68
69 $rawVariationIds = !empty($group->variation_ids) ? array_filter(explode(',', $group->variation_ids)) : [];
70
71 $productIds = array_filter($rawProductIds, function ($id) {
72 return !empty($id) && $id !== 'NULL' && $id !== null;
73 });
74
75 $variationIds = array_filter($rawVariationIds, function ($id) {
76 return !empty($id) && $id !== 'NULL' && $id !== null;
77 });
78
79 if (empty($productIds) && empty($variationIds)) {
80 $this->taskModel->updateMultipleStatus(
81 $taskIds,
82 DiscountTaskModel::STATUS_FAILED,
83 'No valid product or variation IDs found'
84 );
85 return;
86 }
87
88 $this->taskModel->updateMultipleStatus($taskIds, DiscountTaskModel::STATUS_PROCESSING);
89
90 if ($group->action === 'remove') $result = $this->discountManager->remove($productIds, $variationIds);
91 else {
92 $result = $this->discountManager->apply(
93 $group->discount_percent,
94 $productIds,
95 $variationIds,
96 $group->active_days
97 );
98 }
99
100 if ($result && isset($result['status_code']) && $result['status_code'] === 202) {
101 $this->taskModel->deleteMultipleTasks($taskIds);
102 } else {
103 $errorMessage = 'خطای ناشناخته';
104 if ($result) {
105 $decodedBody = is_string($result['body']) ? json_decode($result['body'], true) : $result['body'];
106 if (isset($decodedBody['message'])) {
107 $errorMessage = $decodedBody['message'];
108 } elseif (isset($decodedBody['error'])) {
109 $errorMessage = $decodedBody['error'];
110 } elseif (isset($result['status_code'])) {
111 $errorMessage = sprintf('API returned status code: %d', $result['status_code']);
112 }
113 }
114
115 $this->taskModel->updateMultipleStatus($taskIds, DiscountTaskModel::STATUS_FAILED, $errorMessage);
116 }
117 }
118
119 public function addDiscountTasks($items)
120 {
121 $jobExists = $this->jobManager->getCountJobs(['job_type' => 'sync_basalam_discount_tasks', 'status' => ['pending', 'processing']]);
122 $discountReductionPercent = absint($this->settingsAccessor->getSettings(SettingsConfig::DISCOUNT_REDUCTION_PERCENT));
123
124 if ($jobExists === 0) $this->jobManager->createJob('sync_basalam_discount_tasks', 'pending');
125
126 $createdCount = 0;
127 foreach ($items as $item) {
128 $wcProductId = $item['product_id'] ?? null;
129 $wcVariationId = $item['variation_id'] ?? null;
130 $action = $item['action'] ?? 'apply';
131 $discountPercent = $item['discount_percent'] ?? 0;
132
133 if ($action === 'apply') {
134 $discountPercent = $this->getAdjustedDiscountPercent($discountPercent, $discountReductionPercent);
135 }
136
137 $activeDays = $item['active_days'] ?? $this->settingsAccessor->getSettings(SettingsConfig::DISCOUNT_DURATION) ?? 7;
138
139 $basalamProductId = null;
140 $basalamVariationIds = [null];
141
142 if ($wcProductId) $basalamProductId = get_post_meta($wcProductId, ProductMetaKey::basalamProductId(), true);
143 if ($wcVariationId) {
144 $basalamVariationIds = array_unique(array_filter((array) get_post_meta($wcVariationId, 'sync_basalam_variation_id', false)));
145 }
146
147 foreach ($basalamVariationIds as $basalamVariationId) {
148 if (!$basalamProductId && !$basalamVariationId) continue;
149 $taskData = [
150 'product_id' => $basalamProductId,
151 'variation_id' => $basalamVariationId,
152 'discount_percent' => $discountPercent,
153 'active_days' => $activeDays,
154 'action' => $action,
155 'status' => DiscountTaskModel::STATUS_PENDING,
156 ];
157
158 $taskId = $this->taskModel->create($taskData);
159
160 if ($taskId) {
161 $createdCount++;
162 $this->markProductsAsDiscounted([$wcProductId], [$wcVariationId], $action);
163 }
164 }
165 }
166
167 return $createdCount > 0;
168 }
169
170 private function getAdjustedDiscountPercent($discountPercent, int $discountReductionPercent): float
171 {
172 $discountPercent = (float) $discountPercent;
173
174 if ($discountReductionPercent > 0 && $discountPercent > $discountReductionPercent) {
175 return $discountPercent - $discountReductionPercent;
176 }
177
178 return $discountPercent;
179 }
180
181 public function handleProductDiscount($productId)
182 {
183 $priceField = $this->settingsAccessor->getSettings(SettingsConfig::PRODUCT_PRICE_FIELD);
184
185 $product = wc_get_product($productId);
186
187 if (!$product) return;
188
189 // اتصال �
190 شترک با �
191 حصول دیگر یعنی تخفیف روی �
192 حصول اشتباهی در باسلا�
193 اع�
194 ال �
195 ی‌شود.
196 if (ProductConnection::hasConflict($productId)) return;
197
198 $items = [];
199
200 if ($priceField !== 'sale_strikethrough_price') {
201 $this->handleNonSalePriceMode($product);
202 return;
203 }
204
205 if ($product->is_type('simple')) {
206 $basalamProductId = get_post_meta($productId, ProductMetaKey::basalamProductId(), true);
207
208 if (!$basalamProductId) return;
209
210 $salePrice = $product->get_sale_price();
211 $regularPrice = $product->get_regular_price();
212
213 if ($salePrice && $regularPrice) {
214 $discountPercent = DiscountManager::calculateDiscountPercent($regularPrice, $salePrice);
215 if ($discountPercent > 0) {
216 $items[] = [
217 'product_id' => $productId,
218 'discount_percent' => $discountPercent,
219 'action' => 'apply'
220 ];
221 }
222 } else {
223 $items[] = [
224 'product_id' => $productId,
225 'action' => 'remove'
226 ];
227 }
228 } elseif ($product->is_type('variable')) {
229 $basalamProductId = get_post_meta($productId, ProductMetaKey::basalamProductId(), true);
230 if (!$basalamProductId) return;
231
232 foreach ($product->get_children() as $variationId) {
233 $variation = wc_get_product($variationId);
234 if (!$variation) continue;
235
236 $basalamVariationId = get_post_meta($variationId, 'sync_basalam_variation_id', true);
237 if (!$basalamVariationId) continue;
238
239 $salePrice = $variation->get_sale_price();
240 $regularPrice = $variation->get_regular_price();
241
242 if ($salePrice && $regularPrice) {
243 $discountPercent = DiscountManager::calculateDiscountPercent($regularPrice, $salePrice);
244 if ($discountPercent > 0) {
245 $items[] = [
246 'product_id' => $productId,
247 'variation_id' => $variationId,
248 'discount_percent' => $discountPercent,
249 'action' => 'apply'
250 ];
251 }
252 } else {
253 $items[] = [
254 'product_id' => $productId,
255 'variation_id' => $variationId,
256 'action' => 'remove'
257 ];
258 }
259 }
260 }
261
262 if (!empty($items)) {
263 $this->addDiscountTasks($items);
264 }
265 }
266
267 private function markProductsAsDiscounted(array $wcProductIds, array $wcVariationIds, string $action)
268 {
269 foreach ($wcProductIds as $wcProductId) {
270 if ($wcProductId) {
271 if ($action === 'apply') update_post_meta($wcProductId, 'sync_basalam_discounted', 'true');
272 else delete_post_meta($wcProductId, 'sync_basalam_discounted');
273 }
274 }
275
276 foreach ($wcVariationIds as $wcVariationId) {
277 if ($wcVariationId) {
278 if ($action === 'apply') update_post_meta($wcVariationId, 'sync_basalam_discounted', 'true');
279 else delete_post_meta($wcVariationId, 'sync_basalam_discounted');
280 }
281 }
282 }
283
284 private function handleNonSalePriceMode($product)
285 {
286 $productId = $product->get_id();
287 $items = [];
288
289 if ($product->is_type('simple')) {
290 $basalamProductId = get_post_meta($productId, ProductMetaKey::basalamProductId(), true);
291 if (!$basalamProductId) return;
292
293 $isDiscounted = get_post_meta($productId, 'sync_basalam_discounted', true);
294
295 if ($isDiscounted === 'true') {
296 $items[] = [
297 'product_id' => $productId,
298 'action' => 'remove'
299 ];
300 }
301 } elseif ($product->is_type('variable')) {
302 $basalamProductId = get_post_meta($productId, ProductMetaKey::basalamProductId(), true);
303
304 if (!$basalamProductId) return;
305
306 foreach ($product->get_children() as $variationId) {
307 $basalamVariationId = get_post_meta($variationId, 'sync_basalam_variation_id', true);
308
309 if (!$basalamVariationId) continue;
310
311 $isDiscounted = get_post_meta($variationId, 'sync_basalam_discounted', true);
312
313 if ($isDiscounted === 'true') {
314 $items[] = [
315 'product_id' => $productId,
316 'variation_id' => $variationId,
317 'action' => 'remove'
318 ];
319 }
320 }
321 }
322
323 if (!empty($items)) $this->addDiscountTasks($items);
324 }
325 }
326