Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/cvetdv.ru/bitrix/modules/sale/lib/helpers/order/builder/ |
Upload File : |
<?php namespace Bitrix\Sale\Helpers\Order\Builder; use Bitrix\Main; use Bitrix\Sale\Internals; use Bitrix\Sale\PropertyValue; use Bitrix\Sale\PropertyValueCollection; use Bitrix\Sale\BasketItem; use Bitrix\Sale\Shipment; use Bitrix\Main\ArgumentNullException; use Bitrix\Main\Error; use Bitrix\Main\Localization\Loc; use Bitrix\Main\ObjectException; use Bitrix\Sale\Order; use Bitrix\Sale\Helpers\Admin\Blocks\OrderBuyer; use Bitrix\Sale\Payment; use Bitrix\Sale\PaySystem; use Bitrix\Main\Type\Date; use Bitrix\Main\Type\DateTime; use Bitrix\Sale\Registry; use \Bitrix\Sale\Delivery; use Bitrix\Sale\Result; use Bitrix\Sale\Configuration; use Bitrix\Sale\ShipmentItem; use Bitrix\Sale\TradeBindingEntity; use Bitrix\Sale\TradingPlatformTable; Loc::loadLanguageFile($_SERVER['DOCUMENT_ROOT'] . BX_ROOT . '/modules/sale/lib/helpers/admin/blocks/orderbasketshipment.php'); /** * Class OrderBuilder * @package Bitrix\Sale\Helpers\Order\Builder * @internal */ abstract class OrderBuilder { /** @var OrderBuilderExist|OrderBuilderNew */ protected $delegate = null; /** @var BasketBuilder */ protected $basketBuilder = null; /** @var SettingsContainer */ protected $settingsContainer = null; /** @var Order */ protected $order = null; /** @var array */ protected $formData = array(); /** @var ErrorsContainer */ protected $errorsContainer = null; /** @var bool */ protected $isStartField; /** @var Registry */ protected $registry = null; public function __construct(SettingsContainer $settings) { $this->settingsContainer = $settings; $this->errorsContainer = new ErrorsContainer(); $this->errorsContainer->setAcceptableErrorCodes( $this->settingsContainer->getItemValue('acceptableErrorCodes') ); $this->registry = Registry::getInstance(Registry::REGISTRY_TYPE_ORDER); } /** * @param $data * @throws BuildingException */ public function build($data) { $this->initFields($data) ->delegate() ->createOrder() ->setDiscounts() //? ->setFields() ->buildTradeBindings() ->setProperties() ->setUser() ->buildProfile() ->buildBasket() ->buildPayments() ->buildShipments() ->setRelatedProperties() ->setDiscounts() //? ->finalActions(); return $this; } public function setBasketBuilder(BasketBuilder $basketBuilder) { $this->basketBuilder = $basketBuilder; } public function getRegistry() { return $this->registry; } protected function prepareFields(array $fields) { $fields["ID"] = (isset($fields["ID"]) ? (int)$fields["ID"] : 0); return $fields; } public function initFields(array $data) { $data = $this->prepareFields($data); $this->formData = $data; return $this; } public function delegate() { $data = $this->formData; $this->delegate = (int)$data['ID'] > 0 ? new OrderBuilderExist($this) : new OrderBuilderNew($this); return $this; } public function createOrder() { $data = $this->formData; if($this->order = $this->delegate->createOrder($data)) { $this->isStartField = $this->order->isStartField(); } return $this; } protected function getSettableShipmentFields() { $shipmentClassName = $this->registry->getShipmentClassName(); return array_merge(['PROPERTIES'], $shipmentClassName::getAvailableFields()); } protected function getSettablePaymentFields() { $paymentClassName = $this->registry->getPaymentClassName(); return $paymentClassName::getAvailableFields(); } protected function getSettableOrderFields() { return ['RESPONSIBLE_ID', 'USER_DESCRIPTION', 'ORDER_TOPIC', 'ACCOUNT_NUMBER']; } public function setFields() { $fields = $this->getSettableOrderFields(); foreach($fields as $field) { if(isset($this->formData[$field])) { $r = $this->order->setField($field, $this->formData[$field]); if(!$r->isSuccess()) { $this->getErrorsContainer()->addErrors($r->getErrors()); } } } if(isset($this->formData["PERSON_TYPE_ID"]) && intval($this->formData["PERSON_TYPE_ID"]) > 0) { /** @var \Bitrix\Sale\Result $r */ $r = $this->order->setPersonTypeId(intval($this->formData['PERSON_TYPE_ID'])); } else { /** @var \Bitrix\Sale\Result $r */ $r = $this->order->setPersonTypeId( OrderBuyer::getDefaultPersonType( $this->order->getSiteId() ) ); } if(!$r->isSuccess()) { $this->getErrorsContainer()->addErrors($r->getErrors()); } return $this; } public function setProperties() { if (empty($this->formData["PROPERTIES"])) { return $this; } $propCollection = $this->order->getPropertyCollection(); $res = $propCollection->setValuesFromPost( $this->formData, $this->settingsContainer->getItemValue('propsFiles') ); if (!$res->isSuccess()) { $this->getErrorsContainer()->addErrors($res->getErrors()); } return $this; } public function setRelatedProperties() { if (empty($this->formData["PROPERTIES"])) { return $this; } $propCollection = $this->order->getPropertyCollection(); /** @var PropertyValue $propertyValue */ foreach ($propCollection as $propertyValue) { if (!$propertyValue->getRelations()) { continue; } $post = Internals\Input\File::getPostWithFiles($this->formData, $this->settingsContainer->getItemValue('propsFiles')); $res = $propertyValue->setValueFromPost($post); if (!$res->isSuccess()) { $this->getErrorsContainer()->addErrors($res->getErrors()); } } return $this; } public function setUser() { $this->delegate->setUser(); return $this; } public function buildProfile() { if (empty($this->formData["PROPERTIES"]) || empty($this->formData["USER_ID"])) { return $this; } $profileId = $this->formData["USER_PROFILE"]["ID"] ?? 0; $profileName = $this->formData["USER_PROFILE"]["NAME"] ?? ''; $errors = []; \CSaleOrderUserProps::DoSaveUserProfile( $this->getUserId(), $profileId, $profileName, $this->order->getPersonTypeId(), $this->formData["PROPERTIES"], $errors ); foreach ($errors as $error) { $this->errorsContainer->addError(new Main\Error($error['TEXT'], $error['CODE'], 'PROFILE')); } return $this; } public function setDiscounts() { if(isset($this->formData["DISCOUNTS"]) && is_array($this->formData["DISCOUNTS"])) { $this->order->getDiscount()->setApplyResult($this->formData["DISCOUNTS"]); $r = $this->order->getDiscount()->calculate(); if($r->isSuccess()) { $discountData = $r->getData(); $this->order->applyDiscount($discountData); } } return $this; } public function buildBasket() { $this->delegate->buildBasket(); return $this; } protected function createEmptyShipment() { $shipments = $this->order->getShipmentCollection(); return $shipments->createItem(); } protected function removeShipments() { if($this->getSettingsContainer()->getItemValue('deleteShipmentIfNotExists')) { $shipmentCollection = $this->order->getShipmentCollection(); $shipmentIds = []; foreach($this->formData["SHIPMENT"] as $shipmentData) { if(!isset($shipmentData['ID'])) continue; $shipment = $shipmentCollection->getItemById($shipmentData['ID']); if ($shipment == null) continue; $shipmentIds[] = $shipment->getId(); } foreach ($shipmentCollection as $shipment) { if($shipment->isSystem()) continue; if(!in_array($shipment->getId(), $shipmentIds)) { $r = $shipment->delete(); if (!$r->isSuccess()) { $this->errorsContainer->addErrors($r->getErrors()); return false; } } } } return true; } protected function prepareFieldsStatusId($isNew, $item, $defaultFields) { $statusId = ''; if($isNew) { $deliveryStatusClassName = $this->registry->getDeliveryStatusClassName(); $statusId = $deliveryStatusClassName::getInitialStatus(); } elseif (isset($item['STATUS_ID']) && $item['STATUS_ID'] !== $defaultFields['STATUS_ID']) { $statusId = $item['STATUS_ID']; } return $statusId; } public function buildShipments() { $isEmptyShipmentData = empty($this->formData["SHIPMENT"]) || !is_array($this->formData["SHIPMENT"]); if ($isEmptyShipmentData) { $this->formData["SHIPMENT"] = []; } if ($isEmptyShipmentData && !$this->getSettingsContainer()->getItemValue('createDefaultShipmentIfNeed')) { return $this; } if($isEmptyShipmentData && $this->getOrder()->isNew()) { $this->createEmptyShipment(); return $this; } if(!$this->removeShipments()) { throw new BuildingException(); } $shipmentCollection = $this->order->getShipmentCollection(); foreach($this->formData["SHIPMENT"] as $item) { $shipmentId = intval($item['ID'] ?? 0); $isNew = ($shipmentId <= 0); $deliveryService = null; $storeId = null; if (!isset($item['DEDUCTED']) || $item['DEDUCTED'] !== 'Y') { $item['DEDUCTED'] = 'N'; } $extraServices = isset($item['EXTRA_SERVICES']) && is_array($item['EXTRA_SERVICES']) ? $item['EXTRA_SERVICES'] : [] ; $settableShipmentFields = $this->getSettableShipmentFields(); if (!empty($settableShipmentFields)) { //for backward compatibility $product = $item['PRODUCT'] ?? null; $storeId = (int)($item['DELIVERY_STORE_ID'] ?? 0); $item = array_intersect_key($item, array_flip($settableShipmentFields)); if ($product !== null) { $item['PRODUCT'] = $product; } unset($product); } if($isNew) { $shipment = $shipmentCollection->createItem(); } else { $shipment = $shipmentCollection->getItemById($shipmentId); if(!$shipment) { $this->errorsContainer->addError(new Error(Loc::getMessage("SALE_HLP_ORDERBUILDER_SHIPMENT_NOT_FOUND")." - ".$shipmentId)); continue; } } $defaultFields = $shipment->getFieldValues(); /** @var \Bitrix\Sale\BasketItem $product */ $systemShipment = $shipmentCollection->getSystemShipment(); $systemShipmentItemCollection = $systemShipment->getShipmentItemCollection(); //We suggest that if products is null - ShipmentBasket not loaded yet, if array ShipmentBasket loaded, but empty. $products = null; if( !isset($item['PRODUCT']) && $shipment->getId() <= 0 ) { $products = array(); $basket = $this->order->getBasket(); if($basket) { $basketItems = $basket->getBasketItems(); foreach($basketItems as $product) { $systemShipmentItem = $systemShipmentItemCollection->getItemByBasketCode($product->getBasketCode()); if($product->isBundleChild() || !$systemShipmentItem || $systemShipmentItem->getQuantity() <= 0) continue; $products[] = array( 'AMOUNT' => $systemShipmentItem->getQuantity(), 'BASKET_CODE' => $product->getBasketCode(), ); } } } elseif (isset($item['PRODUCT']) && is_array($item['PRODUCT'])) { $products = $item['PRODUCT']; } if($item['DEDUCTED'] == 'Y' && $products !== null) { $basketResult = $this->buildShipmentBasket($shipment, $products); if(!$basketResult->isSuccess()) { $this->errorsContainer->addErrors($basketResult->getErrors()); } } $shipmentFields = array( 'COMPANY_ID' => (isset($item['COMPANY_ID']) && intval($item['COMPANY_ID']) > 0) ? intval($item['COMPANY_ID']) : 0, 'DEDUCTED' => $item['DEDUCTED'] ?? 'N', 'DELIVERY_DOC_NUM' => $item['DELIVERY_DOC_NUM'] ?? '', 'TRACKING_NUMBER' => $item['TRACKING_NUMBER'] ?? '', 'CURRENCY' => $this->order->getCurrency(), 'COMMENTS' => $item['COMMENTS'] ?? '', ); if (!empty($item['IS_REALIZATION'])) { $shipmentFields['IS_REALIZATION'] = $item['IS_REALIZATION']; } if (!empty($item['ACCOUNT_NUMBER'])) { $shipmentFields['ACCOUNT_NUMBER'] = $item['ACCOUNT_NUMBER']; } if (!empty($item['XML_ID'])) { $shipmentFields['XML_ID'] = $item['XML_ID']; } $statusId = $this->prepareFieldsStatusId($isNew, $item, $defaultFields); if ($statusId !== '') { $shipmentFields['STATUS_ID'] = $statusId; } if (empty($item['COMPANY_ID'])) { $shipmentFields['COMPANY_ID'] = $this->order->getField('COMPANY_ID'); } if (empty($item['RESPONSIBLE_ID'])) { $shipmentFields['RESPONSIBLE_ID'] = $this->order->getField('RESPONSIBLE_ID'); $shipmentFields['EMP_RESPONSIBLE_ID'] = $this->getCurrentUserId(); } $deliveryId = 0; if (isset($item['PROFILE_ID']) && (int)$item['PROFILE_ID'] > 0) { $deliveryId = (int)$item['PROFILE_ID']; } elseif (isset($item['DELIVERY_ID'])) { $deliveryId = (int)$item['DELIVERY_ID']; } elseif ($shipment->getField('DELIVERY_ID')) { $deliveryId = $shipment->getField('DELIVERY_ID'); } $shipmentFields['DELIVERY_ID'] = $deliveryId; $dateFields = ['DELIVERY_DOC_DATE', 'DATE_DEDUCTED', 'DATE_MARKED', 'DATE_CANCELED', 'DATE_RESPONSIBLE_ID']; foreach($dateFields as $fieldName) { if(isset($item[$fieldName])) { if (is_string($item[$fieldName])) { try { $shipmentFields[$fieldName] = new DateTime($item[$fieldName]); } catch (ObjectException $exception) { $this->errorsContainer->addError(new Error('Wrong field "'.$fieldName.'"')); } } elseif ($item[$fieldName] instanceof Date) { $shipmentFields[$fieldName] = $item[$fieldName]; } } } try { if($deliveryService = Delivery\Services\Manager::getObjectById($shipmentFields['DELIVERY_ID'])) { if($deliveryService->isProfile()) { $shipmentFields['DELIVERY_NAME'] = $deliveryService->getNameWithParent(); } else { $shipmentFields['DELIVERY_NAME'] = $deliveryService->getName(); } } } catch (ArgumentNullException $e) { $this->errorsContainer->addError(new Error(Loc::getMessage('SALE_HLP_ORDERBUILDER_DELIVERY_NOT_FOUND'), 'OB_DELIVERY_NOT_FOUND')); return $this; } $responsibleId = $shipment->getField('RESPONSIBLE_ID'); if (($item['RESPONSIBLE_ID'] ?? null) !== $responsibleId || empty($responsibleId)) { if (isset($item['RESPONSIBLE_ID'])) { $shipmentFields['RESPONSIBLE_ID'] = $item['RESPONSIBLE_ID']; } else { $shipmentFields['RESPONSIBLE_ID'] = $this->order->getField('RESPONSIBLE_ID'); } if (!empty($shipmentFields['RESPONSIBLE_ID'])) { $shipmentFields['EMP_RESPONSIBLE_ID'] = $this->getCurrentUserId(); } } if($extraServices) { $shipment->setExtraServices($extraServices); } $setFieldsResult = $shipment->setFields($shipmentFields); if(!$setFieldsResult->isSuccess()) { $this->errorsContainer->addErrors($setFieldsResult->getErrors()); } // region Properties if (isset($item['PROPERTIES']) && is_array($item['PROPERTIES'])) { /** @var PropertyValueCollection $propCollection */ $propCollection = $shipment->getPropertyCollection(); $res = $propCollection->setValuesFromPost($item, []); if (!$res->isSuccess()) { foreach ($res->getErrors() as $error) { $this->getErrorsContainer()->addError( new Main\Error($error->getMessage(), $error->getCode(), 'SHIPMENT_PROPERTIES') ); } } /** @var \Bitrix\Sale\PropertyValue $propValue */ foreach ($propCollection as $propValue) { if ($propValue->isUtil()) { continue; } $property = $propValue->getProperty(); $relatedDeliveryIds = (isset($property['RELATION']) && is_array($property['RELATION'])) ? array_column( array_filter( $property['RELATION'], function ($item) { return $item['ENTITY_TYPE'] === 'D'; } ), 'ENTITY_ID' ) : []; if ( !empty($relatedDeliveryIds) && !in_array($shipment->getField('DELIVERY_ID'), $relatedDeliveryIds) ) { continue; } $res = $propValue->verify(); if (!$res->isSuccess()) { foreach ($res->getErrors() as $error) { $this->getErrorsContainer()->addError( new Main\Error($error->getMessage(), $propValue->getPropertyId(), 'SHIPMENT_PROPERTIES') ); } } $res = $propValue->checkRequiredValue($propValue->getPropertyId(), $propValue->getValue()); if (!$res->isSuccess()) { foreach ($res->getErrors() as $error) { $this->getErrorsContainer()->addError( new Main\Error($error->getMessage(), $propValue->getPropertyId(), 'SHIPMENT_PROPERTIES') ); } } } } // endregion if ($storeId) { $shipment->setStoreId($storeId); } if($item['DEDUCTED'] == 'N' && $products !== null) { $basketResult = $this->buildShipmentBasket($shipment, $products); if(!$basketResult->isSuccess()) { $this->errorsContainer->addErrors($basketResult->getErrors()); } } $isCustomPrice = false; if (isset($item['CUSTOM_PRICE_DELIVERY'])) { $isCustomPrice = $item['CUSTOM_PRICE_DELIVERY'] === 'Y'; } $fields = array( 'CUSTOM_PRICE_DELIVERY' => $isCustomPrice ? 'Y' : 'N', 'PRICE_DELIVERY' => (float)str_replace(',', '.', $item['PRICE_DELIVERY'] ?? 0), ); if (isset($item['ALLOW_DELIVERY'])) { $fields['ALLOW_DELIVERY'] = $item['ALLOW_DELIVERY'] === 'Y' ? 'Y' : 'N'; } if (isset($item['BASE_PRICE_DELIVERY'])) { $fields['BASE_PRICE_DELIVERY'] = (float)str_replace(',', '.', $item['BASE_PRICE_DELIVERY']); } $shipment = $this->delegate->setShipmentPriceFields($shipment, $fields); if($deliveryService && !empty($item['ADDITIONAL'])) { $modifiedShipment = $deliveryService->processAdditionalInfoShipmentEdit($shipment, $item['ADDITIONAL']); $registry = Registry::getInstance(Registry::REGISTRY_TYPE_ORDER); if ($modifiedShipment && get_class($modifiedShipment) == $registry->getShipmentClassName()) { $shipment = $modifiedShipment; } } } return $this; } protected function removeShipmentItems(\Bitrix\Sale\Shipment $shipment, $products, $idsFromForm) { $result = new Result(); $shipmentItemCollection = $shipment->getShipmentItemCollection(); /** @var \Bitrix\Sale\ShipmentItem $shipmentItem */ foreach ($shipmentItemCollection as $shipmentItem) { if (!array_key_exists($shipmentItem->getBasketCode(), $idsFromForm)) { /** @var Result $r */ $r = $shipmentItem->delete(); if (!$r->isSuccess()) { $result->addErrors($r->getErrors()); } } $shipmentItemStoreCollection = $shipmentItem->getShipmentItemStoreCollection(); if ($shipmentItemStoreCollection) { /** @var \Bitrix\Sale\ShipmentItemStore $shipmentItemStore */ foreach ($shipmentItemStoreCollection as $shipmentItemStore) { $shipmentItemId = $shipmentItemStore->getId(); if (!isset($idsFromForm[$shipmentItem->getBasketCode()]['BARCODE_IDS'][$shipmentItemId])) { $delResult = $shipmentItemStore->delete(); if (!$delResult->isSuccess()) { $result->addErrors($delResult->getErrors()); } } } } } return $result; } /** * @param Shipment $shipment * @param array $shipmentBasket * @return Result * @throws ArgumentNullException * @throws \Bitrix\Main\ArgumentException * @throws \Bitrix\Main\ArgumentOutOfRangeException * @throws \Bitrix\Main\NotSupportedException * @throws \Bitrix\Main\ObjectNotFoundException * @throws \Bitrix\Main\SystemException * @throws \Exception */ public function buildShipmentBasket(&$shipment, $shipmentBasket) { /**@var \Bitrix\Sale\Shipment $shipment */ $result = new Result(); $shippingItems = array(); $idsFromForm = array(); $basket = $this->order->getBasket(); $shipmentItemCollection = $shipment->getShipmentItemCollection(); $useStoreControl = Configuration::useStoreControl(); if(is_array($shipmentBasket)) { // PREPARE DATA FOR SET_FIELDS foreach ($shipmentBasket as $items) { $items['QUANTITY'] = floatval(str_replace(',', '.', $items['QUANTITY'])); $items['AMOUNT'] = floatval(str_replace(',', '.', $items['AMOUNT'])); $r = $this->prepareDataForSetFields($shipment, $items); if($r->isSuccess()) { $items = $r->getData()[0]; } else { $result->addErrors($r->getErrors()); return $result; } if (isset($items['BASKET_ID']) && !BasketBuilder::isBasketItemNew($items['BASKET_ID'])) { if (!$basketItem = $basket->getItemById($items['BASKET_ID'])) { $result->addError( new Error( Loc::getMessage('SALE_ORDER_SHIPMENT_BASKET_BASKET_ITEM_NOT_FOUND', array( '#BASKET_ITEM_ID#' => $items['BASKET_ID'], )), 'PROVIDER_UNRESERVED_SHIPMENT_ITEM_WRONG_BASKET_ITEM') ); return $result; } /** @var \Bitrix\Sale\BasketItem $basketItem */ $basketCode = $basketItem->getBasketCode(); } else { $basketCode = $items['BASKET_CODE']; if(!$basketItem = $basket->getItemByBasketCode($basketCode)) { $result->addError( new Error( Loc::getMessage('SALE_ORDER_SHIPMENT_BASKET_BASKET_ITEM_NOT_FOUND', array( '#BASKET_ITEM_ID#' => $basketCode, )), 'PROVIDER_UNRESERVED_SHIPMENT_ITEM_WRONG_BASKET_ITEM') ); return $result; } } $isSupportedMarkingCode = false; if (isset($items['IS_SUPPORTED_MARKING_CODE'])) { $isSupportedMarkingCode = $items['IS_SUPPORTED_MARKING_CODE'] === 'Y'; } $tmp = [ 'BASKET_CODE' => $basketCode, 'AMOUNT' => $items['AMOUNT'] ?? 0, 'ORDER_DELIVERY_BASKET_ID' => $items['ORDER_DELIVERY_BASKET_ID'] ?? 0, 'IS_SUPPORTED_MARKING_CODE' => $isSupportedMarkingCode ? 'Y' : 'N', ]; if (array_key_exists('XML_ID', $items)) { $tmp['XML_ID'] = $items['XML_ID']; } $idsFromForm[$basketCode] = array(); if ( isset($items['BARCODE_INFO']) && $items['BARCODE_INFO'] && ($useStoreControl || $isSupportedMarkingCode) ) { foreach ($items['BARCODE_INFO'] as $item) { if (!$basketItem->isReservableItem()) { $shippingItems[] = $tmp; continue; } $barcodeQuantity = ($basketItem->isBarcodeMulti() || $basketItem->isSupportedMarkingCode()) ? 1 : $item['QUANTITY']; $barcodeStoreId = $item['STORE_ID']; $tmp['BARCODE'] = array( 'ORDER_DELIVERY_BASKET_ID' => $items['ORDER_DELIVERY_BASKET_ID'] ?? 0, 'STORE_ID' => $barcodeStoreId, 'QUANTITY' => $barcodeQuantity, ); $tmp['BARCODE_INFO'] = [ $item['STORE_ID'] => [ 'STORE_ID' => (int)$barcodeStoreId, 'QUANTITY' => (float)$barcodeQuantity, ], ]; $barcodeCount = 0; if ($item['BARCODE']) { foreach ($item['BARCODE'] as $barcode) { $barcode['ID'] = (int)($barcode['ID'] ?? 0); $tmp['BARCODE_INFO'][$barcodeStoreId]['BARCODE'] = [$barcode]; if (isset($barcode['MARKING_CODE'])) { $barcode['MARKING_CODE'] = (string)$barcode['MARKING_CODE']; } else { $barcode['MARKING_CODE'] = ''; } $idsFromForm[$basketCode]['BARCODE_IDS'][$barcode['ID']] = true; if ($barcode['ID'] > 0) { $tmp['BARCODE']['ID'] = $barcode['ID']; } else { unset($tmp['BARCODE']['ID']); } $tmp['BARCODE']['BARCODE'] = (string)$barcode['VALUE']; $tmp['BARCODE']['MARKING_CODE'] = $barcode['MARKING_CODE']; $shippingItems[] = $tmp; $barcodeCount++; } } elseif (!$basketItem->isBarcodeMulti() && !$basketItem->isSupportedMarkingCode()) { $shippingItems[] = $tmp; continue; } if ($basketItem->isBarcodeMulti() || $basketItem->isSupportedMarkingCode()) { while ($barcodeCount < $item['QUANTITY']) { unset($tmp['BARCODE']['ID']); $tmp['BARCODE']['BARCODE'] = ''; $tmp['BARCODE']['MARKING_CODE'] = ''; $shippingItems[] = $tmp; $barcodeCount++; } } } } else { $shippingItems[] = $tmp; } } } // DELETE FROM COLLECTION $r = $this->removeShipmentItems($shipment, $shipmentBasket, $idsFromForm); if(!$r->isSuccess()) $result->addErrors($r->getErrors()); $isStartField = $shipmentItemCollection->isStartField(); // SET DATA foreach ($shippingItems as $shippingItem) { if ((int)$shippingItem['ORDER_DELIVERY_BASKET_ID'] <= 0) { $basketCode = $shippingItem['BASKET_CODE']; /** @var \Bitrix\Sale\Order $this->order */ $basketItem = $this->order->getBasket()->getItemByBasketCode($basketCode); /** @var \Bitrix\Sale\BasketItem $basketItem */ $shipmentItem = $shipmentItemCollection->createItem($basketItem); if ($shipmentItem === null) { $result->addError( new Error( Loc::getMessage('SALE_ORDER_SHIPMENT_BASKET_ERROR_ALREADY_SHIPPED') ) ); return $result; } unset($shippingItem['BARCODE']['ORDER_DELIVERY_BASKET_ID']); } else { $shipmentItem = $shipmentItemCollection->getItemById($shippingItem['ORDER_DELIVERY_BASKET_ID']); if($shipmentItem) { $basketItem = $shipmentItem->getBasketItem(); } else //It's a possible case when we are creating new shipment. { /** @var \Bitrix\Crm\Order\Shipment $systemShipment */ $systemShipment = $shipment->getCollection()->getSystemShipment(); /** @var \Bitrix\Crm\Order\ShipmentItemCollection $systemShipmentItemCollection */ $systemShipmentItemCollection = $systemShipment->getShipmentItemCollection(); $shipmentItem = $systemShipmentItemCollection->getItemById($shippingItem['ORDER_DELIVERY_BASKET_ID']); if($shipmentItem) { $basketItem = $shipmentItem->getBasketItem(); $shipmentItem = $shipmentItemCollection->createItem($basketItem); $shipmentItem->setField('QUANTITY', $shipmentItem->getField('QUANTITY')); } else { $result->addError( new Error( Loc::getMessage('SALE_HLP_ORDERBUILDER_SHIPMENT_ITEM_ERROR',[ '#ID#' => $shippingItem['ORDER_DELIVERY_BASKET_ID'], ]) ) ); continue; } } } if ($shippingItem['AMOUNT'] <= 0) { $result->addError( new Error( Loc::getMessage('SALE_ORDER_SHIPMENT_BASKET_ERROR_QUANTITY', array('#BASKET_ITEM#' => $basketItem->getField('NAME'))), 'BASKET_ITEM_'.$basketItem->getBasketCode() ) ); continue; } $r = $this->modifyQuantityShipmentItem($shipmentItem, $shippingItem); if(!$r->isSuccess()) $result->addErrors($r->getErrors()); if (array_key_exists('XML_ID', $shippingItem)) { $setFieldResult = $shipmentItem->setField('XML_ID', $shippingItem['XML_ID']); if (!$setFieldResult->isSuccess()) { $result->addErrors($setFieldResult->getErrors()); } } } if ($isStartField) { $hasMeaningfulFields = $shipmentItemCollection->hasMeaningfulField(); /** @var Result $r */ $r = $shipmentItemCollection->doFinalAction($hasMeaningfulFields); if (!$r->isSuccess()) { $result->addErrors($r->getErrors()); } } return $result; } protected function prepareDataForSetFields(\Bitrix\Sale\Shipment $shipment, $items) { $result = new Result(); return $result->setData([$items]); } protected function modifyQuantityShipmentItem(ShipmentItem $shipmentItem, array $params) { $result = new Result(); if ($shipmentItem->getQuantity() < $params['AMOUNT']) { $this->order->setMathActionOnly(true); $setFieldResult = $shipmentItem->setField('QUANTITY', $params['AMOUNT']); $this->order->setMathActionOnly(false); if (!$setFieldResult->isSuccess()) { $result->addErrors($setFieldResult->getErrors()); } } $r = $this->setBarcodeShipmentItem($shipmentItem, $params); if($r->isSuccess() == false) { $result->addErrors($r->getErrors()); } $setFieldResult = $shipmentItem->setField('QUANTITY', $params['AMOUNT']); if (!$setFieldResult->isSuccess()) { $result->addErrors($setFieldResult->getErrors()); } return $result; } protected function setBarcodeShipmentItem(ShipmentItem $shipmentItem, array $params) { $result = new Result(); $basketItem = $shipmentItem->getBasketItem(); $useStoreControl = Configuration::useStoreControl(); if ( !empty($params['BARCODE']) && ($useStoreControl || $params['IS_SUPPORTED_MARKING_CODE'] === 'Y' ) && $basketItem->isReservableItem() ) { $barcode = $params['BARCODE']; /** @var \Bitrix\Sale\ShipmentItemStoreCollection $shipmentItemStoreCollection */ $shipmentItemStoreCollection = $shipmentItem->getShipmentItemStoreCollection(); if ($shipmentItemStoreCollection) { if (!$basketItem->isBarcodeMulti() && !$basketItem->isSupportedMarkingCode()) { /** @var Result $r */ $r = $shipmentItemStoreCollection->setBarcodeQuantityFromArray($params); if (!$r->isSuccess()) { $result->addErrors($r->getErrors()); } } if (isset($barcode['ID']) && intval($barcode['ID']) > 0) { /** @var \Bitrix\Sale\ShipmentItemStore $shipmentItemStore */ if ($shipmentItemStore = $shipmentItemStoreCollection->getItemById($barcode['ID'])) { unset($barcode['ID']); $setFieldResult = $shipmentItemStore->setFields($barcode); if (!$setFieldResult->isSuccess()) { $result->addErrors($setFieldResult->getErrors()); } } } else { $shipmentItemStore = $shipmentItemStoreCollection->createItem($basketItem); $setFieldResult = $shipmentItemStore->setFields($barcode); if (!$setFieldResult->isSuccess()) { $result->addErrors($setFieldResult->getErrors()); } } } } return $result; } protected function createEmptyPayment() { $innerPaySystem = PaySystem\Manager::getObjectById(PaySystem\Manager::getInnerPaySystemId()); $paymentCollection = $this->order->getPaymentCollection(); $payment = $paymentCollection->createItem($innerPaySystem); $payment->setField('SUM', $this->order->getPrice()); return $payment; } protected function removePayments() { if($this->getSettingsContainer()->getItemValue('deletePaymentIfNotExists')) { $paymentCollection = $this->order->getPaymentCollection(); $paymentIds = []; foreach($this->formData["PAYMENT"] as $paymentData) { if(!isset($paymentData['ID'])) continue; $payment = $paymentCollection->getItemById($paymentData['ID']); if ($payment == null) continue; $paymentIds[] = $payment->getId(); } foreach ($paymentCollection as $payment) { if(!in_array($payment->getId(), $paymentIds)) { $r = $payment->delete(); if (!$r->isSuccess()) { $this->errorsContainer->addErrors($r->getErrors()); return false; } } } } return true; } /** * @return bool */ protected function isEmptyPaymentData(): bool { return empty($this->formData["PAYMENT"]) || !is_array($this->formData["PAYMENT"]); } /** * @return bool */ protected function needCreateDefaultPayment(): bool { return $this->getSettingsContainer()->getItemValue('createDefaultPaymentIfNeed'); } public function buildPayments() { $isEmptyPaymentData = $this->isEmptyPaymentData(); if ($isEmptyPaymentData) { $this->formData['PAYMENT'] = []; } if ($isEmptyPaymentData && !$this->needCreateDefaultPayment()) { return $this; } if($isEmptyPaymentData && $this->getOrder()->isNew()) { $this->createEmptyPayment(); return $this; } if(!$this->removePayments()) { $this->errorsContainer->addError(new Error('Payments remove - error')); throw new BuildingException(); } $paymentCollection = $this->order->getPaymentCollection(); foreach($this->formData["PAYMENT"] as $paymentData) { $paymentId = (int)($paymentData['ID'] ?? 0); $isNew = ($paymentId <= 0); $hasError = false; $products = $paymentData['PRODUCT'] ?? []; $settablePaymentFields = $this->getSettablePaymentFields(); if(count($settablePaymentFields)>0)//for backward compatibility $paymentData = array_intersect_key($paymentData, array_flip($settablePaymentFields)); /** @var \Bitrix\Sale\Payment $paymentItem */ if($isNew) { $paymentItem = $paymentCollection->createItem(); if (isset($paymentData['CURRENCY']) && !empty($paymentData['CURRENCY']) && $paymentData['CURRENCY'] !== $this->order->getCurrency()) { $paymentData["SUM"] = \CCurrencyRates::ConvertCurrency($paymentData["SUM"], $paymentData["CURRENCY"], $this->order->getCurrency()); $paymentData['CURRENCY'] = $this->order->getCurrency(); } } else { $paymentItem = $paymentCollection->getItemById($paymentId); if(!$paymentItem) { $this->errorsContainer->addError(new Error(Loc::getMessage("SALE_HLP_ORDERBUILDER_PAYMENT_NOT_FOUND")." - ".$paymentId)); continue; } } $isReturn = (isset($paymentData['IS_RETURN']) && ($paymentData['IS_RETURN'] == 'Y' || $paymentData['IS_RETURN'] == 'P')); $psService = null; if((int)$paymentData['PAY_SYSTEM_ID'] > 0) { $psService = PaySystem\Manager::getObjectById((int)$paymentData['PAY_SYSTEM_ID']); $paymentData['PAY_SYSTEM_NAME'] = ($psService) ? $psService->getField('NAME') : ''; } if (isset($paymentData['COMPANY_ID'])) { $paymentData['COMPANY_ID'] = (int)$paymentData['COMPANY_ID']; } if (isset($paymentData['PAID'])) { $paymentFields['PAID'] = ($paymentData['PAID'] === 'Y') ? 'Y' : 'N'; unset($paymentData['PAID']); } if ($isNew) { if(empty($paymentData['COMPANY_ID'])) { $paymentData['COMPANY_ID'] = $this->order->getField('COMPANY_ID'); } if(empty($paymentData['RESPONSIBLE_ID'])) { $paymentData['RESPONSIBLE_ID'] = $this->order->getField('RESPONSIBLE_ID'); $paymentData['EMP_RESPONSIBLE_ID'] = $this->getCurrentUserId(); } } $dateFields = ['DATE_PAID', 'DATE_PAY_BEFORE', 'DATE_BILL', 'PAY_RETURN_DATE', 'PAY_VOUCHER_DATE']; foreach($dateFields as $fieldName) { if(isset($paymentData[$fieldName]) && is_string($paymentData[$fieldName])) { try { $paymentData[$fieldName] = new Date($paymentData[$fieldName]); } catch (ObjectException $exception) { $this->errorsContainer->addError(new Error('Wrong field "'.$fieldName.'"')); $hasError = true; } } } if($paymentItem->isPaid() && isset($paymentData['SUM']) && abs(floatval($paymentData['SUM']) - floatval($paymentItem->getSum())) > 0.001) { $this->errorsContainer->addError(new Error(Loc::getMessage("SALE_HLP_ORDERBUILDER_ERROR_PAYMENT_SUM"))); $hasError = true; } /* * We are editing an order. We have only one payment. So the payment fields are mostly in view mode. * If we have changed the price of the order then the sum of the payment must be changed automaticaly by payment api earlier. * But if the payment sum was received from the form we will erase the previous changes. */ if(isset($paymentData['SUM'])) { $paymentData['SUM'] = (float)str_replace(',', '.', $paymentData['SUM']); } if(isset($paymentData['RESPONSIBLE_ID'])) { $paymentData['RESPONSIBLE_ID'] = !empty($paymentData['RESPONSIBLE_ID']) ? $paymentData['RESPONSIBLE_ID'] : $this->getCurrentUserId(); if($paymentData['RESPONSIBLE_ID'] != $paymentItem->getField('RESPONSIBLE_ID')) { if(!$isNew) { $paymentData['EMP_RESPONSIBLE_ID'] = $this->getCurrentUserId(); } } } if(!$hasError) { if($paymentItem->isInner() && isset($paymentData['SUM']) && $paymentData['SUM'] === 0) { unset($paymentData['SUM']); } $setResult = $paymentItem->setFields($paymentData); if(!$setResult->isSuccess()) { $this->errorsContainer->addErrors($setResult->getErrors()); } if ($products) { $this->buildPayableItems($paymentItem, $products); } if($isReturn && $paymentData['IS_RETURN']) { $setResult = $paymentItem->setReturn($paymentData['IS_RETURN']); if(!$setResult->isSuccess()) { $this->errorsContainer->addErrors($setResult->getErrors()); } } if(!empty($paymentFields['PAID'])) { $setResult = $paymentItem->setPaid($paymentFields['PAID']); if(!$setResult->isSuccess()) { $this->errorsContainer->addErrors($setResult->getErrors()); } } } } return $this; } /** * @param Payment $payment * @param array $payableItems * @return Result * @throws ArgumentNullException * @throws Main\ArgumentException * @throws Main\ArgumentOutOfRangeException * @throws Main\ArgumentTypeException * @throws Main\NotImplementedException * @throws Main\SystemException */ public function buildPayableItems(Payment $payment, array $payableItems): Result { $result = new Result(); $basket = $this->order->getBasket(); $payableItemCollection = $payment->getPayableItemCollection(); foreach ($payableItems as $item) { $payableItem = null; if (isset($item['BASKET_CODE'])) { /** @var BasketItem $basketItem */ $basketItem = $basket->getItemByBasketCode($item['BASKET_CODE']); if ($basketItem) { $payableItem = $payableItemCollection->createItemByBasketItem($basketItem); } } elseif (isset($item['DELIVERY_ID'])) { /** @var Shipment $shipment */ foreach ($this->order->getShipmentCollection()->getNotSystemItems() as $shipment) { if ( $shipment->getId() === 0 && (int)$item['DELIVERY_ID'] === $shipment->getDeliveryId() ) { $payableItem = $payableItemCollection->createItemByShipment($shipment); } } } if ($payableItem === null) { continue; } $quantity = floatval(str_replace(',', '.', $item['QUANTITY'])); $payableItem->setField('QUANTITY', $quantity); } return $result; } public function buildTradeBindings() { if(!isset($this->formData["TRADE_BINDINGS"])) { return $this; } if(!$this->removeTradeBindings()) { return $this; } if(isset($this->formData["TRADE_BINDINGS"]) && count($this->formData["TRADE_BINDINGS"])>0) { $tradeBindingCollection = $this->order->getTradeBindingCollection(); foreach($this->formData["TRADE_BINDINGS"] as $fields) { $tradingPlatformId = (int)($fields['TRADING_PLATFORM_ID'] ?? 0); if ($tradingPlatformId === 0) { continue; } $r = $this->tradingPlatformExists($tradingPlatformId); if($r->isSuccess()) { $id = (int)($fields['ID'] ?? 0); $isNew = ($id <= 0); if($isNew) { $binding = $tradeBindingCollection->createItem(); } else { $binding = $tradeBindingCollection->getItemById($id); if(!$binding) { $this->errorsContainer->addError(new Error('Can\'t find Trade Binding with id:"'.$id.'"', 'TRADE_BINDING_NOT_EXISTS')); continue; } } $fields = array_intersect_key($fields, array_flip(TradeBindingEntity::getAvailableFields())); $r = $binding->setFields($fields); } if(!$r->isSuccess()) $this->errorsContainer->addErrors($r->getErrors()); } } return $this; } protected function tradingPlatformExists($id) { $r = new Result(); $platformFields = TradingPlatformTable::getById($id)->fetchAll(); if (!isset($platformFields[0])) { $r->addError(new Error('tradingPlatform is not exists')); } return $r; } protected function removeTradeBindings() { if($this->getSettingsContainer()->getItemValue('deleteTradeBindingIfNotExists')) { $tradeBindingCollection = $this->order->getTradeBindingCollection(); $internalIx = []; foreach($this->formData["TRADE_BINDINGS"] as $tradeBinding) { if(!isset($tradeBinding['ID'])) continue; $binding = $tradeBindingCollection->getItemById($tradeBinding['ID']); if ($binding == null) continue; $internalIx[] = $binding->getId(); } foreach ($tradeBindingCollection as $binding) { if(!in_array($binding->getId(), $internalIx)) { $r = $binding->delete(); if (!$r->isSuccess()) { $this->errorsContainer->addErrors($r->getErrors()); return false; } } } } return true; } public function finalActions() { if($this->isStartField) { $hasMeaningfulFields = $this->order->hasMeaningfulField(); $r = $this->order->doFinalAction($hasMeaningfulFields); if(!$r->isSuccess()) { $this->errorsContainer->addErrors($r->getErrors()); } } return $this; } public function getOrder() { return $this->order; } public function getSettingsContainer() { return $this->settingsContainer; } public function getErrorsContainer() { return $this->errorsContainer; } public function getFormData($fieldName = '') { if($fieldName <> '') { $result = $this->formData[$fieldName] ?? null; } else { $result = $this->formData; } return $result; } public function getBasketBuilder() { return $this->basketBuilder; } public static function getDefaultPersonType($siteId) { $personTypes = self::getBuyerTypesList($siteId); reset($personTypes); return key($personTypes); } public static function getBuyerTypesList($siteId) { static $result = array(); if(!isset($result[$siteId])) { $result[$siteId] = array(); $res = \Bitrix\Sale\Internals\PersonTypeTable::getList(array( 'order' => array('SORT' => 'ASC', 'NAME' => 'ASC'), 'filter' => array('=ACTIVE' => 'Y', '=PERSON_TYPE_SITE.SITE_ID' => $siteId), )); while ($personType = $res->fetch()) { $result[$siteId][$personType["ID"]] = htmlspecialcharsbx($personType["NAME"])." [".$personType["ID"]."]"; } } return $result[$siteId]; } public function getUserId() { $userId = (int)($this->formData["USER_ID"] ?? 0); if($userId > 0) { return $userId; } $userId = 0; $settingValue = (int)$this->getSettingsContainer()->getItemValue('createUserIfNeed'); if ($settingValue === \Bitrix\Sale\Helpers\Order\Builder\SettingsContainer::SET_ANONYMOUS_USER) { $userId = \CSaleUser::GetAnonymousUserID(); } elseif ($settingValue === \Bitrix\Sale\Helpers\Order\Builder\SettingsContainer::ALLOW_NEW_USER_CREATION) { $userId = $this->createUserFromFormData(); } if ($userId > 0 && empty($this->formData["USER_ID"])) { $this->formData["USER_ID"] = $userId; } return $userId; } /** * @return false|int|mixed|string|null * @throws \Bitrix\Main\ArgumentException * @throws \Bitrix\Main\ArgumentOutOfRangeException * @throws \Bitrix\Main\NotImplementedException * @throws \Bitrix\Main\ObjectPropertyException * @throws \Bitrix\Main\SystemException */ protected function createUserFromFormData() { $errors = []; $orderProps = $this->order->getPropertyCollection(); if ($email = $orderProps->getUserEmail()) { $email = $email->getValue(); } if ($name = $orderProps->getPayerName()) { $name = $name->getValue(); } if ($phone = $orderProps->getPhone()) { $phone = $phone->getValue(); } if ($this->getSettingsContainer()->getItemValue('searchExistingUserOnCreating')) { $userId = $this->searchExistingUser($email, $phone); } if (!isset($userId)) { $userId = $this->searchExistingUser($email, $phone); } if (!isset($userId)) { $userId = \CSaleUser::DoAutoRegisterUser( $email, $name, $this->formData['SITE_ID'], $errors, [ 'PERSONAL_PHONE' => $phone, 'PHONE_NUMBER' => $phone, ] ); if (!empty($errors)) { foreach ($errors as $val) { $this->errorsContainer->addError(new Error($val['TEXT'], 0, 'USER')); } } else { // ToDo remove it? when to authorize buyer? global $USER; $USER->Authorize($userId); } } return $userId; } /** * @param $email * @param $phone * @return int|null * @throws \Bitrix\Main\ArgumentException * @throws \Bitrix\Main\ObjectPropertyException * @throws \Bitrix\Main\SystemException */ private function searchExistingUser($email, $phone): ?int { $existingUserId = null; if (!empty($email)) { $res = Main\UserTable::getRow([ 'filter' => [ '=ACTIVE' => 'Y', '=EMAIL' => $email, ], 'select' => ['ID'], ]); if (isset($res['ID'])) { $existingUserId = (int)$res['ID']; } } if (!$existingUserId && !empty($phone)) { $normalizedPhone = NormalizePhone($phone); $normalizedPhoneForRegistration = Main\UserPhoneAuthTable::normalizePhoneNumber($phone); if (!empty($normalizedPhone)) { $res = Main\UserTable::getRow([ 'filter' => [ 'ACTIVE' => 'Y', [ 'LOGIC' => 'OR', '=PHONE_AUTH.PHONE_NUMBER' => $normalizedPhoneForRegistration, '=PERSONAL_PHONE' => $normalizedPhone, '=PERSONAL_MOBILE' => $normalizedPhone, ], ], 'select' => ['ID'], ]); if (isset($res['ID'])) { $existingUserId = (int)$res['ID']; } } } return $existingUserId; } protected function getCurrentUserId(): ?int { global $USER; $currentUserId = null; if (isset($USER) && $USER instanceof \CUser) { $currentUserId = (int)$USER->GetID(); if ($currentUserId <= 0) { $currentUserId = null; } } return $currentUserId; } }