3namespace Sale\Handlers\Delivery\YandexTaxi\ClaimBuilder;
5use Bitrix\Location\Entity\Address\FieldCollection;
6use Bitrix\Location\Entity\Location;
7use Bitrix\Location\Entity;
10use Bitrix\Main\Localization\Loc;
11use Bitrix\Main\Result;
12use Bitrix\Sale\Delivery\ExtraServices\Manager;
13use Bitrix\Sale\Delivery\Services\RecipientDataProvider;
14use Bitrix\Sale\Shipment;
15use Bitrix\Sale\ShipmentItem;
16use Sale\Handlers\Delivery\YandexTaxi\Api\RequestEntity\Address;
17use Sale\Handlers\Delivery\YandexTaxi\Api\RequestEntity\Claim;
18use Sale\Handlers\Delivery\YandexTaxi\Api\RequestEntity\ClientRequirements;
19use Sale\Handlers\Delivery\YandexTaxi\Api\RequestEntity\Contact;
20use Sale\Handlers\Delivery\YandexTaxi\Api\RequestEntity\RoutePoint;
21use Sale\Handlers\Delivery\YandexTaxi\Api\RequestEntity\ShippingItem;
22use Sale\Handlers\Delivery\YandexTaxi\Api\RequestEntity\TransportClassification;
23use Sale\Handlers\Delivery\YandexTaxi\Api\Tariffs\Repository;
24use Sale\Handlers\Delivery\YandexTaxi\Common\OrderEntitiesCodeDictionary;
25use Sale\Handlers\Delivery\YandexTaxi\Common\ReferralSourceBuilder;
26use Bitrix\Main\PhoneNumber;
27use Sale\Handlers\Delivery\YandexTaxi;
28use Bitrix\Location\Entity\Address\Field;
29use Sale\Handlers\Delivery\YandexTaxi\ServiceContainer;
36final class ClaimBuilder
38 public const SOURCE_ROUTE_POINT_ID = 1;
39 private const SOURCE_ROUTE_POINT_VISIT_ORDER = 1;
40 private const SOURCE_ROUTE_POINT_TYPE =
'source';
42 public const DESTINATION_ROUTE_POINT_ID = 2;
43 private const DESTINATION_ROUTE_POINT_VISIT_ORDER = 2;
44 private const DESTINATION_ROUTE_POINT_TYPE =
'destination';
47 protected $tariffsRepository;
50 protected $referralSourceBuilder;
60 public function __construct(
61 Repository $tariffsRepository,
62 ReferralSourceBuilder $referralSourceBuilder
65 $this->tariffsRepository = $tariffsRepository;
66 $this->referralSourceBuilder = $referralSourceBuilder;
73 public function build(Shipment $shipment): Result
75 $this->result =
new Result();
82 $contactFrom = $this->buildContactFrom($shipment);
83 if (is_null($contactFrom))
88 $contactTo = $this->buildContactTo($shipment);
89 if (is_null($contactTo))
97 $addressFromResult = $this->buildAddressFrom($shipment);
98 if (!$addressFromResult->isSuccess())
100 return $this->result->addErrors($addressFromResult->getErrors());
103 $addressFrom = $addressFromResult->getData()[
'ADDRESS'];
104 $addressFrom->setId(self::SOURCE_ROUTE_POINT_ID);
106 $addressToResult = $this->buildAddressTo($shipment);
107 if (!$addressToResult->isSuccess())
109 return $this->result->addErrors($addressToResult->getErrors());
112 $addressTo = $addressToResult->getData()[
'ADDRESS'];
113 $addressTo->setId(self::DESTINATION_ROUTE_POINT_ID);
118 $commentForDriver = $this->getPropertyValue(
120 OrderEntitiesCodeDictionary::COMMENT_FOR_DRIVER_PROPERTY_CODE
122 if ($commentForDriver)
124 $claim->setComment($commentForDriver);
127 $buildClientReqResult = $this->buildTransportClassification($shipment);
128 if (!$buildClientReqResult->isSuccess())
130 return $this->result->addErrors($buildClientReqResult->getErrors());
132 $clientRequirements = $buildClientReqResult->getData()[
'REQUIREMENTS'];
137 if (!$this->isDoorDeliveryRequired($shipment))
139 $claim->setSkipDoorToDoor(
true);
143 ->setEmergencyContact($contactFrom)
144 ->setClientRequirements($clientRequirements)
147 ->setPointId(self::SOURCE_ROUTE_POINT_ID)
148 ->setVisitOrder(self::SOURCE_ROUTE_POINT_VISIT_ORDER)
149 ->
setType(self::SOURCE_ROUTE_POINT_TYPE)
150 ->setContact($contactFrom)
151 ->setAddress($addressFrom)
152 ->setSkipConfirmation(
true),
154 ->setPointId(self::DESTINATION_ROUTE_POINT_ID)
155 ->setVisitOrder(self::DESTINATION_ROUTE_POINT_VISIT_ORDER)
156 ->
setType(self::DESTINATION_ROUTE_POINT_TYPE)
157 ->setContact($contactTo)
158 ->setAddress($addressTo)
159 ->setSkipConfirmation(
true)
162 $this->referralSourceBuilder->getReferralSourceValue()
165 $shippingItemCollection = $this->getShippingItemCollection($shipment);
166 $validationResult = $shippingItemCollection->isValid();
167 if (!$validationResult->isSuccess())
169 return $this->result->addErrors($validationResult->getErrors());
172 foreach ($shippingItemCollection as $shippingItem)
174 $claim->addItem($shippingItem);
177 return $this->result->setData([
'RESULT' => $claim]);
184 public function getShippingItemCollection(Shipment $shipment): ShippingItemCollection
186 $result =
new ShippingItemCollection();
189 foreach($shipment->getShipmentItemCollection()->getShippableItems() as $shipmentItem)
191 $basketItem = $shipmentItem->getBasketItem();
199 ->
setTitle((
string)$basketItem->getField(
'NAME'))
200 ->setCostValue((
string)((
float)$basketItem->getPriceWithVat() * $shipmentItem->getQuantity()))
201 ->setCostCurrency((
string)$basketItem->getCurrency())
202 ->setQuantity((
int)ceil($shipmentItem->getQuantity()))
203 ->setPickupPoint(self::SOURCE_ROUTE_POINT_ID)
204 ->setDropoffPoint(self::DESTINATION_ROUTE_POINT_ID)
215 private function getExtraServiceValues(Shipment $shipment):
array
219 $extraServicesValues = is_array($shipment->getExtraServices()) ? $shipment->getExtraServices() : [];
221 $services = Manager::getExtraServicesList($shipment->getDeliveryId());
222 foreach ($services as $serviceId => $serviceItem)
224 $initValue = isset($serviceItem[
'INIT_VALUE']) ? $serviceItem[
'INIT_VALUE'] :
null;
226 $value = isset($extraServicesValues[$serviceId]) ? $extraServicesValues[$serviceId] : $initValue;
228 $result[$serviceItem[
'CODE']] = [
230 'SERVICE' => $serviceItem,
241 public function isDoorDeliveryRequired(Shipment $shipment)
243 $extraServiceValues = $this->getExtraServiceValues($shipment);
245 foreach ($extraServiceValues as
$code => $item)
247 if (
$code != OrderEntitiesCodeDictionary::DOOR_DELIVERY_EXTRA_SERVICE_CODE)
252 return ($item[
'VALUE'] ===
'Y');
262 public static function isOffersCalculateMethod(
string $taxiClass): bool
265 in_array($taxiClass, [
'express',
'cargo'])
266 && ServiceContainer::getRegionFinder()->getCurrentRegion() ===
'ru'
274 public function buildClientRequirements(Shipment $shipment): Result
276 $requirements =
new ClientRequirements();
278 $result = $this->getTaxiClass($shipment);
280 if (!$result->isSuccess())
285 $tariff = $result->getData()[
'tariff'];
287 $requirements->setTaxiClasses([$tariff[
'name']]);
288 $requirements->setSkipDoorToDoor(!$this->isDoorDeliveryRequired($shipment));
290 $options = $this->makeOptions($shipment, $tariff);
291 $requirements->setOptions(
$options);
293 $result->setData([
'REQUIREMENTS' => $requirements]);
302 public function buildTransportClassification(Shipment $shipment): Result
304 $requirements =
new TransportClassification();
306 $result = $this->getTaxiClass($shipment);
308 if (!$result->isSuccess())
313 $tariff = $result->getData()[
'tariff'];
315 $requirements->setTaxiClass($tariff[
'name']);
317 $options = $this->makeOptions($shipment, $tariff);
318 $requirements->setOptions(
$options);
320 $result->setData([
'REQUIREMENTS' => $requirements]);
329 protected function buildContactFrom(Shipment $shipment): ?Contact
331 $responsibleUser = $this->getResponsibleUser($shipment);
333 if (!$responsibleUser)
335 $this->result->addError(
new Error(Loc::getMessage(
'SALE_YANDEX_TAXI_RESPONSIBLE_NOT_SPECIFIED')));
339 $responsibleUserName = $this->getResponsibleUserName($responsibleUser);
340 $responsibleUserEmail = $this->getResponsibleUserEmail($responsibleUser);
341 $responsibleUserPhone = $this->getResponsibleUserPhone($responsibleUser);
346 if (!$responsibleUserName)
348 $this->result->addError(
new Error(Loc::getMessage(
'SALE_YANDEX_TAXI_RESPONSIBLE_FULL_NAME_NOT_SPECIFIED')));
351 if (!$responsibleUserPhone)
353 $this->result->addError(
new Error(Loc::getMessage(
'SALE_YANDEX_TAXI_RESPONSIBLE_PHONE_NOT_SPECIFIED')));
357 $oResponsibleUserPhone = PhoneNumber\Parser::getInstance()->parse($responsibleUserPhone);
358 if (!$oResponsibleUserPhone->isValid())
360 $this->result->addError(
364 Loc::getMessage(
'SALE_YANDEX_TAXI_RESPONSIBLE_PHONE_NOT_VALID'),
365 (
string)$oResponsibleUserPhone->format()
372 if (!$responsibleUserEmail)
374 $this->result->addError(
new Error(Loc::getMessage(
'SALE_YANDEX_TAXI_RESPONSIBLE_EMAIL_NOT_SPECIFIED')));
378 return (
new Contact())
379 ->setName($responsibleUserName)
383 ->setEmail($responsibleUserEmail);
390 protected function buildContactTo(Shipment $shipment): ?Contact
394 if (is_null($recipientContact))
396 $this->result->addError(
398 Loc::getMessage(
'SALE_YANDEX_TAXI_CLIENT_CLIENT_CONTACT_NOT_FOUND')
404 if (!$recipientContact->getName())
406 $this->result->addError(
408 Loc::getMessage(
'SALE_YANDEX_TAXI_CLIENT_FULL_NAME_NOT_SPECIFIED')
414 $recipientContactPhones = $recipientContact->getPhones();
415 if (empty($recipientContactPhones))
417 $this->result->addError(
419 Loc::getMessage(
'SALE_YANDEX_TAXI_CLIENT_PHONE_NOT_SPECIFIED')
426 $oPhone = PhoneNumber\Parser::getInstance()->parse($recipientContactPhones[0]->
getValue());
427 if (!$oPhone->isValid())
429 $this->result->addError(
433 Loc::getMessage(
'SALE_YANDEX_TAXI_CLIENT_PHONE_NOT_VALID'),
434 (
string)$oPhone->format()
442 return (
new Contact())
443 ->setName($recipientContact->getName())
451 private function getResponsibleUserName(
array $responsibleUser)
453 return trim(sprintf(
'%s %s', $responsibleUser[
'NAME'], $responsibleUser[
'LAST_NAME']));
460 private function getResponsibleUserEmail(
array $responsibleUser)
462 return isset($responsibleUser[
'EMAIL']) ? (string)$responsibleUser[
'EMAIL'] : null;
469 private function getResponsibleUserPhone(
array $responsibleUser)
471 if (isset($responsibleUser[
'WORK_PHONE']) && !empty($responsibleUser[
'WORK_PHONE']))
473 return (
string)$responsibleUser[
'WORK_PHONE'];
475 elseif (isset($responsibleUser[
'PERSONAL_MOBILE']) && !empty($responsibleUser[
'PERSONAL_MOBILE']))
477 return (
string)$responsibleUser[
'PERSONAL_MOBILE'];
479 elseif (isset($responsibleUser[
'PERSONAL_PHONE']) && !empty($responsibleUser[
'PERSONAL_PHONE']))
481 return (
string)$responsibleUser[
'PERSONAL_PHONE'];
491 public function buildAddressFrom(Shipment $shipment): Result
493 return $this->buildAddress($shipment,
'IS_ADDRESS_FROM');
500 public function buildAddressTo(Shipment $shipment): Result
502 return $this->buildAddress($shipment,
'IS_ADDRESS_TO');
509 public function getTaxiClass(Shipment $shipment): Result
511 $result =
new Result();
513 $deliveryService = $shipment->getDelivery();
514 if (!$deliveryService)
516 return $result->addError(
517 new Error(Loc::getMessage(
'SALE_YANDEX_TAXI_DELIVERY_SERVICE_NOT_FOUND'))
521 $deliveryServiceConfig = $deliveryService->getConfig();
522 if (!isset($deliveryServiceConfig[
'MAIN'][
'ITEMS'][
'PROFILE_TYPE'][
'VALUE']))
524 return $result->addError(
525 new Error(Loc::getMessage(
'SALE_YANDEX_TAXI_TARIFF_IS_NOT_SPECIFIED'))
528 $tariffCode = $deliveryServiceConfig[
'MAIN'][
'ITEMS'][
'PROFILE_TYPE'][
'VALUE'];
531 $availableTariffs = $this->tariffsRepository->getTariffs();
532 foreach ($availableTariffs as $availableTariff)
534 if ($availableTariff[
'name'] === $tariffCode)
536 $tariff = $availableTariff;
541 if (is_null($tariff))
543 return $result->addError(
544 new Error(Loc::getMessage(
'SALE_YANDEX_TAXI_TARIFF_HAS_NOT_BEEN_FOUND'))
548 $result->setData([
'tariff' => $tariff]);
558 private function makeOptions(Shipment $shipment,
array $tariff):
array
560 $extraServiceValues = $this->getExtraServiceValues($shipment);
563 foreach ($tariff[
'supported_requirements'] as $supportedRequirement)
565 if ($supportedRequirement[
'type'] ===
'multi_select')
567 foreach ($supportedRequirement[
'options'] as $srOption)
569 if (isset($extraServiceValues[$srOption[
'value']])
570 && $extraServiceValues[$srOption[
'value']][
'VALUE'] ===
'Y'
573 if (!is_array(
$options[$supportedRequirement[
'name']]))
575 $options[$supportedRequirement[
'name']] = [];
577 $options[$supportedRequirement[
'name']][] = $srOption[
'value'];
581 elseif ($supportedRequirement[
'type'] ===
'select')
583 if (isset($extraServiceValues[$supportedRequirement[
'name']])
584 && !empty($extraServiceValues[$supportedRequirement[
'name']][
'VALUE'])
587 foreach ($supportedRequirement[
'options'] as $srOption)
592 if ($srOption[
'value'] == $extraServiceValues[$supportedRequirement[
'name']][
'VALUE'])
594 $options[$supportedRequirement[
'name']] = $srOption[
'value'];
610 private function buildAddress(Shipment $shipment,
string $attribute)
612 $result =
new Result();
614 if (!Loader::includeModule(
'location'))
616 return $result->addError(
617 new Error(Loc::getMessage(
'SALE_YANDEX_TAXI_LOCATION_MODULE_REQUIRED'))
621 $property = $shipment->getPropertyCollection()->getAttribute($attribute);
622 $addressArray = $property ? $property->getValue() :
null;
623 if (!is_array($addressArray) || empty($addressArray))
625 return $result->addError(
628 'SALE_YANDEX_TAXI_FIELD_VALUE_NOT_SPECIFIED',
630 '#FIELD_NAME#' => $property ? $property->getName() :
''
637 $address = Entity\Address::fromArray($addressArray);
638 $addressFieldCollection = $address->getFieldCollection();
641 $countryField = $addressFieldCollection->getItemByType(Location\Type::COUNTRY);
644 $cityField = $addressFieldCollection->getItemByType(Location\Type::LOCALITY);
647 $streetField = $addressFieldCollection->getItemByType(Location\Type::STREET);
650 $buildingField = $addressFieldCollection->getItemByType(Location\Type::BUILDING);
652 return $result->setData(
654 'ADDRESS' => (
new YandexTaxi\Api\RequestEntity\Address())
655 ->setCountry($countryField ? $countryField->getValue() :
'')
656 ->setCity($cityField ? $cityField->getValue() :
'')
657 ->setStreet($streetField ? $streetField->getValue() :
'')
658 ->setBuilding($buildingField ? $buildingField->getValue() :
'')
660 $this->getAddressFieldValues(
661 $addressFieldCollection,
662 function (Field $field)
665 $field->getType() > Location\Type::ADDRESS_LINE_1
671 $this->getAddressFieldValues(
672 $addressFieldCollection,
673 function (Field $field)
676 $field->getType() >= Location\Type::COUNTRY
677 && $field->getType() <= Location\Type::ADDRESS_LINE_1
682 ->setCoordinates([(
float)$address->getLongitude(), (
float)$address->getLatitude()])
692 private function getAddressFieldValues(FieldCollection $addressFieldCollection, \Closure $fieldsFilter): string
697 static function (Field $field)
699 return $field->getValue();
702 $addressFieldCollection->getSortedItems(),
714 private function getPropertyValue(Shipment $shipment,
string $propertyCode)
716 $propertyValueObject = $this->getPropertyValueObject($shipment, $propertyCode);
717 if (!$propertyValueObject)
722 return $propertyValueObject->getValue();
730 private function getPropertyValueObject(Shipment $shipment,
string $propertyCode)
732 $propertyValueCollection = $shipment->getPropertyCollection();
740 if ($property->getField(
'CODE') !== $propertyCode)
755 private function getResponsibleUser(Shipment $shipment): ?
array
759 $userId = (int)$shipment->getField(
'RESPONSIBLE_ID');
765 $result = \CUser::GetList(
771 return $result ??
null;
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
static format(PhoneNumber $number, $formatType, $forceNationalPrefix=false)
static getContact(Shipment $shipment)
</td ></tr ></table ></td ></tr ><?endif?><? $propertyIndex=0;foreach( $arGlobalProperties as $propertyCode=> $propertyValue
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
$GLOBALS['____1690880296']
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)