1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
eventprocessor.php
См. документацию.
1<?php
2
3namespace Sale\Handlers\Delivery\YandexTaxi\EventJournal;
4
5use Bitrix\Main\Localization\Loc;
6use Bitrix\Main\Type\DateTime;
7use Bitrix\Sale\Delivery\Requests\RequestTable;
8use Bitrix\Sale\Repository\ShipmentRepository;
9use Sale\Handlers\Delivery\YandexTaxi\Api\Api;
10use Sale\Handlers\Delivery\YandexTaxi\Api\ApiResult\Journal\Event;
11use Sale\Handlers\Delivery\YandexTaxi\Api\ApiResult\Journal\PriceChanged;
12use Sale\Handlers\Delivery\YandexTaxi\Api\ApiResult\Journal\StatusChanged;
13use Sale\Handlers\Delivery\YandexTaxi\Api\RequestEntity\Claim;
14use Sale\Handlers\Delivery\YandexTaxi\Api\RequestEntity\RoutePoint;
15use Sale\Handlers\Delivery\YandexTaxi\Api\StatusDictionary;
16use Sale\Handlers\Delivery\YandexTaxi\Internals\ClaimsTable;
17use Bitrix\Sale\Delivery\Services;
18use Bitrix\Sale\Delivery\Requests;
19use Bitrix\Sale\Delivery\Requests\Message;
20use Bitrix\Sale\Internals\Analytics\Storage;
21use Bitrix\Sale\Delivery\Internals\Analytics\Provider;
22use Bitrix\Main\Application;
23
29final class EventProcessor
30{
31 private const DELIVERY_ANALYTICS_CODE = 'yandex_taxi';
32
34 protected $api;
35
37 private $claims = [];
38
40 private $events = [];
41
46 public function __construct(Api $api)
47 {
48 $this->api = $api;
49 }
50
55 public function process(int $serviceId, array $events)
56 {
57 $this->prepareClaims($events);
58 $this->prepareEvents($events);
59 $this->findChanges();
60 $this->applyChanges($serviceId);
61 }
62
66 private function prepareClaims(array $events)
67 {
68 $claimsIds = [];
69
70 foreach ($events as $event)
71 {
72 $claimsIds[$event->getClaimId()] = true;
73 }
74
75 $claims = ClaimsTable::getList(
76 [
77 'filter' => ['=EXTERNAL_ID' => array_keys($claimsIds)],
78 ]
79 )->fetchAll();
80
81 $eventTypes = $this->getKnownEventTypes();
82 foreach ($claims as $claim)
83 {
84 $this->claims[$claim['EXTERNAL_ID']] = [
85 'CURRENT_ITEM' => $claim,
86 'CHANGES' => array_combine($eventTypes, array_fill(0, count($eventTypes), null)),
87 ];
88 }
89 }
90
94 private function prepareEvents(array $events)
95 {
96 foreach ($events as $event)
97 {
98 $claimId = $event->getClaimId();
99 $eventCode = $event->getCode();
100
101 if (!isset($this->events[$claimId]))
102 {
103 $this->events[$claimId] = [];
104 }
105
106
107 if (!isset($this->events[$claimId][$eventCode]))
108 {
109 $this->events[$claimId][$eventCode] = [];
110 }
111 $this->events[$claimId][$eventCode][] = $event;
112 }
113 }
114
118 private function getKnownEventTypes()
119 {
120 return [
121 PriceChanged::EVENT_CODE,
122 StatusChanged::EVENT_CODE,
123 ];
124 }
125
129 private function findChanges()
130 {
131 $eventTypes = $this->getKnownEventTypes();
132 foreach ($this->claims as $claimId => $claimItem)
133 {
134 $claim = $claimItem['CURRENT_ITEM'];
135
136 if (!isset($this->events[$claimId]))
137 {
138 continue;
139 }
140
141 foreach ($eventTypes as $eventType)
142 {
143 if (!isset($this->events[$claimId][$eventType]))
144 {
145 continue;
146 }
147
149 $events = $this->events[$claimId][$eventType];
150
151 foreach ($events as $event)
152 {
153 $eventUpdatedAt = $this->readDate($event->getUpdatedTs());
154 $currentUpdatedAt = $this->readDate($claim['EXTERNAL_UPDATED_TS']);
155
156 if ($currentUpdatedAt >= $eventUpdatedAt)
157 {
158 continue;
159 }
160
162 $latestEvent = $claimItem['CHANGES'][$eventType];
163 if (is_null($latestEvent) ||
164 $this->readDate($latestEvent->getUpdatedTs()) < $eventUpdatedAt
165 )
166 {
167 $this->claims[$claimId]['CHANGES'][$eventType] = $event;
168 }
169 }
170 }
171 }
172 }
173
177 private function applyChanges(int $serviceId): void
178 {
179 foreach ($this->claims as $claimId => $claimItem)
180 {
181 $newUpdatedTs = null;
182 $fields = [];
183 /*** @var Event $event */
184 foreach ($claimItem['CHANGES'] as $eventType => $event)
185 {
186 if (is_null($event))
187 {
188 continue;
189 }
190
191 $fields += $event->provideUpdateFields();
192
193 if (is_null($newUpdatedTs)
194 || $this->readDate($event->getUpdatedTs()) > $this->readDate($newUpdatedTs))
195 {
196 $newUpdatedTs = $event->getUpdatedTs();
197 }
198 }
199
200 if (!$fields)
201 {
202 continue;
203 }
204
205 $this->updateClaim(
206 $serviceId,
207 $claimItem['CURRENT_ITEM']['ID'],
208 $newUpdatedTs,
209 $fields
210 );
211 }
212 }
213
220 private function updateClaim(int $serviceId, int $id, string $newUpdatedTs, array $fields)
221 {
222 $fields = array_merge(
223 $fields,
224 [
225 'UPDATED_AT' => new DateTime(),
226 'EXTERNAL_UPDATED_TS' => $newUpdatedTs,
227 ]
228 );
229
230 $updateResult = ClaimsTable::update($id, $fields);
231 if ($updateResult->isSuccess())
232 {
233 $this->onClaimUpdated($serviceId, $id, $fields);
234 }
235 }
236
242 private function onClaimUpdated(int $serviceId, int $id, array $fields)
243 {
244 $claim = ClaimsTable::getById($id)->fetch();
245 if (!$claim)
246 {
247 return;
248 }
249
250 $deliveryServiceIds = array_column(
251 Services\Manager::getList([
252 'select' => ['ID'],
253 'filter' => ['PARENT_ID' => $serviceId]
254 ])->fetchAll(),
255 'ID'
256 );
257 $deliveryServiceIds[] = $serviceId;
258
259 $request = RequestTable::getList([
260 'filter' => [
261 '=DELIVERY_ID' => $deliveryServiceIds,
262 '=EXTERNAL_ID' => $claim['EXTERNAL_ID'],
263 ],
264 ])->fetch();
265 if (!$request)
266 {
267 return;
268 }
269
270 $shipment = ShipmentRepository::getInstance()->getById($claim['SHIPMENT_ID']);
271 if (!$shipment)
272 {
273 return;
274 }
275 $deliveryName = !is_null($shipment->getDelivery()) ? $shipment->getDelivery()->getName() : '';
276
277 switch ($fields['EXTERNAL_STATUS'])
278 {
279 case StatusDictionary::READY_FOR_APPROVAL:
280 $remoteClaim = $this->requestClaim($claim['EXTERNAL_ID']);
281 if (
282 $remoteClaim
283 && ($pricing = $remoteClaim->getPricing())
284 && ($offer = $pricing->getOffer())
285 )
286 {
287 $price = $offer->getPrice();
288 $currency = $pricing->getCurrency();
289 }
290 else
291 {
292 $price = null;
293 $currency = null;
294 }
295
296 $result = $this->api->acceptClaim($claim['EXTERNAL_ID'], 1);
297 $message = (new Message\Message())->setSubject(Loc::getMessage('SALE_YANDEX_TAXI_ACCEPTING_CLAIM'));
298 $deleteRequest = false;
299
300 if ($result->isSuccess())
301 {
302 if ($price && $currency)
303 {
305 ->setBody(
306 Loc::getMessage('SALE_YANDEX_TAXI_DELIVERY_CALCULATION_RECEIVED_SUCCESSFULLY_V2')
307 )
308 ->setCurrency($currency)
309 ->addMoneyValue('#PRICE#', $price)
310 ;
311 }
312 else
313 {
314 $message->setBody(Loc::getMessage('SALE_YANDEX_TAXI_DELIVERY_CALCULATION_FAILED'));
315 $message->setStatus(new Message\Status(
316 Loc::getMessage('SALE_YANDEX_TAXI_ERROR_STATUS'),
318 ));
319 $deleteRequest = true;
320 }
321 }
322 else
323 {
325 ->setBody(Loc::getMessage('SALE_YANDEX_TAXI_DELIVERY_ACCEPT_CLAIM_ERROR'))
326 ->setStatus(new Message\Status(
327 Loc::getMessage('SALE_YANDEX_TAXI_ERROR_STATUS'),
329 ));
330 $deleteRequest = true;
331 }
332
333 Requests\Manager::sendMessage(
334 Requests\Manager::MESSAGE_MANAGER_ADDRESSEE,
335 $message,
336 $request['ID'],
337 $shipment->getId()
338 );
339 if ($deleteRequest)
340 {
341 Requests\Manager::deleteDeliveryRequest($request['ID']);
342 }
343 break;
344 case StatusDictionary::PERFORMER_FOUND:
345 $remoteClaim = $this->requestClaim($claim['EXTERNAL_ID']);
346 if (!$remoteClaim)
347 {
348 break;
349 }
350
351 $performerInfo = $remoteClaim->getPerformerInfo();
352 if (!$performerInfo)
353 {
354 break;
355 }
356
357 $externalProperties = [
358 [
359 'NAME' => Loc::getMessage('SALE_YANDEX_TAXI_PERFORMER'),
360 'VALUE' => implode(
361 ', ',
362 [
363 $performerInfo->getCourierName(),
364 implode(
365 ' ',
366 [
367 $performerInfo->getCarModel(),
368 $performerInfo->getCarNumber(),
369 ]
370 )
371 ]
372 ),
373 ],
374 ];
375
376 $getPhoneResult = $this->api->getPhone($claim['EXTERNAL_ID']);
377 if ($getPhoneResult->isSuccess())
378 {
379 $externalProperties[] = [
380 'NAME' => Loc::getMessage('SALE_YANDEX_TAXI_DRIVER_PHONE'),
381 'VALUE' => $getPhoneResult->getPhone(),
382 'TAGS' => ['phone'],
383 ];
384
385 $externalProperties[] = [
386 'NAME' => Loc::getMessage('SALE_YANDEX_TAXI_DRIVER_PHONE_EXT'),
387 'VALUE' => $getPhoneResult->getExt(),
388 ];
389 }
390
391 Requests\Manager::updateDeliveryRequest(
392 $request['ID'],
393 [
394 'EXTERNAL_STATUS' => Loc::getMessage('SALE_YANDEX_TAXI_DELIVERY_IN_PROCESS_V2'),
395 'EXTERNAL_STATUS_SEMANTIC' => Requests\Manager::EXTERNAL_STATUS_SEMANTIC_PROCESS,
396 'EXTERNAL_PROPERTIES' => $externalProperties,
397 ]
398 );
399
400 $expectedOnSourceTimestamp = $this->getExpectedOnSourceTimestamp($remoteClaim->getRoutePoints());
401 if ($expectedOnSourceTimestamp)
402 {
403 Requests\Manager::sendMessage(
404 Requests\Manager::MESSAGE_MANAGER_ADDRESSEE,
405 (new Message\Message())
406 ->setSubject(
407 Loc::getMessage('SALE_YANDEX_TAXI_PERFORMER_FOUND')
408 )
409 ->setBody(
410 Loc::getMessage(
411 'SALE_YANDEX_TAXI_PICKUP_TIME',
412 [
413 '#PERSON_NAME#' => $performerInfo->getCourierName()
414 ?: Loc::getMessage('SALE_YANDEX_TAXI_PERFORMER')
415 ,
416 '#DELIVERY_NAME#' => $deliveryName,
417 ]
418 )
419 )
420 ->addDateValue(
421 '#TIME#',
422 $expectedOnSourceTimestamp,
423 Application::getInstance()->getContext()->getCulture()->getShortTimeFormat()
424 )
425 ,
426 $request['ID'],
427 $shipment->getId()
428 );
429 }
430
431 break;
432 case StatusDictionary::PICKUPED:
433 Requests\Manager::sendMessage(
434 Requests\Manager::MESSAGE_RECIPIENT_ADDRESSEE,
435 (new Message\Message())
436 ->setBody(Loc::getMessage('SALE_YANDEX_TAXI_YOUR_ORDER_IS_ON_ITS_WAY'))
437 ->setType(Message\Message::TYPE_SHIPMENT_PICKUPED),
438 $request['ID'],
439 $shipment->getId()
440 );
441 break;
442 case StatusDictionary::PERFORMER_NOT_FOUND:
443 case StatusDictionary::FAILED:
444 case StatusDictionary::ESTIMATING_FAILED:
445 $messagesMap = [
446 StatusDictionary::PERFORMER_NOT_FOUND => [
447 'SUBJECT' => Loc::getMessage('SALE_YANDEX_TAXI_PERFORMER_NOT_FOUND'),
448 'BODY' => Loc::getMessage(
449 'SALE_YANDEX_TAXI_PERFORMER_NOT_FOUND_DESCRIPTION'
450 ),
451 ],
452 StatusDictionary::FAILED => [
453 'SUBJECT' => Loc::getMessage('SALE_YANDEX_TAXI_ERROR_OCCURRED'),
454 'BODY' => Loc::getMessage(
455 'SALE_YANDEX_TAXI_ERROR_OCCURRED_DESCRIPTION'
456 ),
457 ],
458 StatusDictionary::ESTIMATING_FAILED => [
459 'SUBJECT' => Loc::getMessage('SALE_YANDEX_TAXI_ESTIMATING_FAILED'),
460 'BODY' => Loc::getMessage(
461 'SALE_YANDEX_TAXI_ESTIMATING_FAILED_DESCRIPTION'
462 ),
463 ],
464 ];
465
466 Requests\Manager::sendMessage(
467 Requests\Manager::MESSAGE_MANAGER_ADDRESSEE,
468 (new Message\Message())
469 ->setSubject($messagesMap[$fields['EXTERNAL_STATUS']]['SUBJECT'] ?? '')
470 ->setBody($messagesMap[$fields['EXTERNAL_STATUS']]['BODY'] ?? '')
471 ->setStatus(
472 new Message\Status(
473 Loc::getMessage('SALE_YANDEX_TAXI_ERROR_STATUS'),
475 )
476 ),
477 $request['ID'],
478 $shipment->getId()
479 );
480 Requests\Manager::deleteDeliveryRequest($request['ID']);
481 break;
482 case StatusDictionary::CANCELLED_BY_TAXI:
483 Requests\Manager::sendMessage(
484 Requests\Manager::MESSAGE_MANAGER_ADDRESSEE,
485 (new Message\Message())
486 ->setSubject(Loc::getMessage('SALE_YANDEX_TAXI_CANCELLED_BY_PERFORMER'))
487 ->setBody(Loc::getMessage('SALE_YANDEX_TAXI_CANCELLED_BY_PERFORMER_DESCRIPTION'))
488 ->setStatus(
489 new Message\Status(
490 Loc::getMessage('SALE_YANDEX_TAXI_CANCELLATION'),
492 )
493 ),
494 $request['ID'],
495 $shipment->getId()
496 );
497 Requests\Manager::deleteDeliveryRequest($request['ID']);
498 break;
499 case StatusDictionary::RETURNED_FINISH:
500 Requests\Manager::sendMessage(
501 Requests\Manager::MESSAGE_MANAGER_ADDRESSEE,
502 (new Message\Message())
503 ->setSubject(Loc::getMessage('SALE_YANDEX_TAXI_PERFORMER_RETURNED_CARGO'))
504 ->setBody(Loc::getMessage('SALE_YANDEX_TAXI_PERFORMER_RETURNED_CARGO_DESCRIPTION'))
505 ->setStatus(
506 new Message\Status(
507 Loc::getMessage('SALE_YANDEX_TAXI_RETURN'),
509 )
510 ),
511 $request['ID'],
512 $shipment->getId()
513 );
514 Requests\Manager::deleteDeliveryRequest($request['ID']);
515 break;
516 case StatusDictionary::DELIVERED_FINISH:
517 Requests\Manager::updateDeliveryRequest(
518 $request['ID'],
519 [
520 'STATUS' => Requests\Manager::STATUS_PROCESSED,
521 'EXTERNAL_STATUS' => Loc::getMessage('SALE_YANDEX_TAXI_DELIVERY_FINISHED_V2'),
522 'EXTERNAL_STATUS_SEMANTIC' => Requests\Manager::EXTERNAL_STATUS_SEMANTIC_SUCCESS,
523 ]
524 );
525
526 $message = (new Message\Message())->setSubject(
527 Loc::getMessage('SALE_YANDEX_TAXI_PERFORMER_FINISHED_DELIVERY')
528 );
529 $price = isset($claim['EXTERNAL_FINAL_PRICE']) ? (float)$claim['EXTERNAL_FINAL_PRICE'] : null;
530 $currency = isset($claim['EXTERNAL_CURRENCY']) ? (string)$claim['EXTERNAL_CURRENCY'] : null;
531 if (!is_null($price) && !is_null($currency))
532 {
534 ->setBody(
535 Loc::getMessage(
536 'SALE_YANDEX_TAXI_PRICE',
537 [
538 '#DELIVERY_NAME#' => $deliveryName,
539 ]
540 )
541 )
542 ->addMoneyValue('#PRICE#', $price)
543 ->setCurrency($currency)
544 ;
545 }
546 else
547 {
548 $message->setBody(
549 Loc::getMessage(
550 'SALE_YANDEX_TAXI_TARIFF',
551 [
552 '#DELIVERY_NAME#' => $deliveryName,
553 ]
554 )
555 );
556 }
557
558 Requests\Manager::sendMessage(
559 Requests\Manager::MESSAGE_MANAGER_ADDRESSEE,
560 $message,
561 $request['ID'],
562 $shipment->getId()
563 );
564
565 break;
566 }
567
571 if (
572 !is_null($claim['EXTERNAL_RESOLUTION'])
573 || $claim['EXTERNAL_STATUS'] === StatusDictionary::PERFORMER_NOT_FOUND)
574 {
575 ClaimsTable::update($claim['ID'], ['FURTHER_CHANGES_EXPECTED' => 'N']);
576
577 if (
578 isset($claim['EXTERNAL_RESOLUTION'])
579 && $claim['EXTERNAL_RESOLUTION'] === ClaimsTable::EXTERNAL_STATUS_SUCCESS
580 )
581 {
582 $this->saveOrderForAnalytics($claim);
583 if ($shipment->setField('DEDUCTED', 'Y')->isSuccess())
584 {
585 $shipment->getOrder()->save();
586 }
587 }
588 }
589 }
590
594 private function saveOrderForAnalytics(array $claim): void
595 {
596 $order = [
597 'id' => $claim['EXTERNAL_ID'],
598 'is_successful' => 'Y',
599 'status' => $claim['EXTERNAL_STATUS'],
600 'created_at' => $claim['CREATED_AT']->getTimestamp(),
601 ];
602
603 if ($claim['EXTERNAL_FINAL_PRICE'] && $claim['EXTERNAL_CURRENCY'])
604 {
605 $order['amount'] = $claim['EXTERNAL_FINAL_PRICE'];
606 $order['currency'] = $claim['EXTERNAL_CURRENCY'];
607 }
608
609 (new Storage(new Provider(self::DELIVERY_ANALYTICS_CODE, [$order])))->save();
610 }
611
616 private function requestClaim(string $externalId): ?Claim
617 {
618 $getClaimResult = $this->api->getClaim($externalId);
619 $remoteClaim = $getClaimResult->getClaim();
620
621 if ($getClaimResult->isSuccess() && !is_null($remoteClaim))
622 {
623 return $remoteClaim;
624 }
625
626 return null;
627 }
628
633 private function readDate(string $dateTime)
634 {
635 return \DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $dateTime);
636 }
637
642 private function getExpectedOnSourceTimestamp(array $routePoints): ?int
643 {
644 $sourceRoutePoints = array_filter(
645 $routePoints,
646 static function (RoutePoint $routePoint)
647 {
648 return $routePoint->getType() === 'source';
649 }
650 );
651
652 if (empty($sourceRoutePoints))
653 {
654 return null;
655 }
656
657 $sourceRoutePoint = $sourceRoutePoints[0];
658 $visitedAt = $sourceRoutePoint->getVisitedAt();
659 if (!$visitedAt)
660 {
661 return null;
662 }
663
664 $expectedTimeOnSourceRaw = $visitedAt->getExpected();
665 if (!$expectedTimeOnSourceRaw)
666 {
667 return null;
668 }
669
670 $expectedTimeOnSource = $this->readDate($expectedTimeOnSourceRaw);
671 if (!$expectedTimeOnSource)
672 {
673 return null;
674 }
675
676 return $expectedTimeOnSource->getTimestamp();
677 }
678}
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
static getList(array $parameters=[])
Определения manager.php:135
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
setType(string $type)
Определения Param.php:121
$order
Определения payment.php:8
$message
Определения payment.php:8
$event
Определения prolog_after.php:141
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$currency
Определения template.php:266
$fields
Определения yandex_run.php:501