PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.9.2
ووسلام – همگام سازی ووکامرس و باسلام v1.9.2
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 / Products / Discount / DiscountTaskProcessor.php

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

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