<?php
namespace WebBundle\Service;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\ORMException;
use Exception;
use FlexApp\Enum\OrderJobNameEnum;
use FlexApp\Exceptions\EmailAlreadyRegisteredException;
use FlexApp\Service\LogsManager;
use FlexApp\Service\Order\OrderUserService;
use Knp\Bundle\SnappyBundle\Snappy\Response\PdfResponse;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Psr\Log\LoggerInterface;
use SoapFault;
use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use Symfony\Component\Security\Core\Security;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use WebBundle\Constant\CookieKeysConstant;
use WebBundle\Entity\Article;
use WebBundle\Entity\BuyOrder;
use WebBundle\Entity\BuyOrderArticle;
use WebBundle\Entity\ListCountry;
use WebBundle\Entity\OrderAddress;
use WebBundle\Entity\OrderPayment;
use WebBundle\Entity\User;
use WebBundle\Enum\BuyOrderLiftEnum;
use WebBundle\Enum\BuyOrderStatusEnum;
use WebBundle\Enum\CountryNumberEnum;
use WebBundle\Enum\DeliveryTypeTimeEnum;
use WebBundle\Enum\MeasureOrderEnum;
use WebBundle\Enum\PaymentStatusEnum;
use WebBundle\Enum\PaymentTypeEnum;
use WebBundle\Enum\ReasonForResetOrderEnum;
use WebBundle\Enum\SexEnum;
use WebBundle\Enum\VatTypeEnum;
use WebBundle\Filler\OrderFiller;
use WebBundle\Filler\OrderItemsFiller;
use WebBundle\Helper\App;
use WebBundle\Helper\ConversionHelper;
use WebBundle\Helper\CookieHelper;
use WebBundle\Helper\DateHelper;
use WebBundle\Helper\LocaleHelper;
use WebBundle\Helper\OrderHelper;
use WebBundle\Helper\OrderItemHelper;
use WebBundle\Helper\OrderPaymentHelper;
use WebBundle\Helper\PriceHelper;
use WebBundle\Helper\StrHelper;
use WebBundle\Helper\UserHelper;
use WebBundle\Repository\ArticleRepository;
use WebBundle\Repository\BuyOrderRepository;
use WebBundle\Repository\CurrencyRateRepository;
use WebBundle\Repository\ListCountryRepository;
use WebBundle\Repository\ListVatRepository;
use WebBundle\Repository\UserRepository;
use WebBundle\Scenario\ChangeOrderStatusScenario;
use WebBundle\Serializer\Normalizer\OrderListNormalizer;
class OrderService extends ExtendService
{
/** @required */
public OrderFiller $orderFiller;
/** @required */
public OrderItemsFiller $orderItemsFiller;
/** @required */
public CurrencyRateRepository $currencyRateRepository;
/** @required */
public ListVatRepository $listVatRepository;
/** @required */
public WarehouseService $warehouseService;
/** @required */
public BuyOrderManagerService $buyOrderManagerService;
/** @required */
public BuyOrderDeliveryService $buyOrderDeliveryService;
/** @required */
public PortalService $portalService;
/** @required */
public TranslationService $translationService;
/** @required */
public UserService $userService;
/** @required */
public Security $security;
private OrderAddressService $addressService;
private OrderHistoryService $historyService;
private OrderPaymentService $paymentService;
private OrderItemService $itemService;
private OrderOneCService $oneCService;
private SampleService $sampleService;
private OrderUserService $orderUserService;
private AuthorizationChecker $authorizationChecker;
protected bool $asConsole = false;
private ?BuyOrder $oldOrder = null;
private OrderListNormalizer $orderListNormalizer;
private BuyOrderRepository $buyOrderRepository;
private ArticleRepository $articleRepository;
private UserRepository $userRepo;
private ListCountryRepository $listCountryRepository;
private EntityManagerInterface $em;
private LogsManager $logsManager;
private LoggerInterface $logger;
// Метка зеркала от которого отправлен запрос в 1С
protected int $mirror = 0;
protected string $token;
// Если этот ключ равен true, то в 1С будет послан сигнал, что срок должен стать обычным, а не минутным
public bool $timer = false;
private bool $needRunScenario = true;
public function __construct(
AuthorizationChecker $authorizationChecker,
OrderAddressService $addressService,
OrderHistoryService $historyService,
OrderPaymentService $paymentService,
OrderItemService $itemService,
OrderOneCService $oneCService,
SampleService $sampleService,
OrderUserService $orderUserService,
EntityManagerInterface $em,
OrderListNormalizer $orderListNormalizer,
BuyOrderRepository $repo,
ArticleRepository $articleRepository,
UserRepository $userRepository,
ListCountryRepository $listCountryRepository,
LoggerInterface $logger,
LogsManager $logsManager
) {
$this->asConsole = false;
$this->authorizationChecker = $authorizationChecker;
$this->addressService = $addressService;
$this->historyService = $historyService;
$this->paymentService = $paymentService;
$this->itemService = $itemService;
$this->oneCService = $oneCService;
$this->sampleService = $sampleService;
$this->orderUserService = $orderUserService;
$this->em = $em;
$mirror = 1;
if (App::getContainer() && App::getContainer()->hasParameter('mirror')) {
$mirror = App::getParameter('mirror');
}
$this->mirror = $mirror;
$this->orderListNormalizer = $orderListNormalizer;
$this->buyOrderRepository = $repo;
$this->articleRepository = $articleRepository;
$this->userRepo = $userRepository;
$this->listCountryRepository = $listCountryRepository;
$this->logger = $logger;
$this->logsManager = $logsManager;
$this->token = UserHelper::getInstance()->getToken();
}
/**
* @param string $name - название лога
* @param string $hash - идентификатор заказа
* @param string|array $data - данные
* @param string $s - доп метка лога
* @throws Exception
*/
private function log(string $name, string $hash, $data, string $s = '')
{
// Попытка распарсить вложенный JSON
if (
is_array($data)
&& isset($data['value'])
&& is_string($data['value'])
) {
$decoded = json_decode($data['value'], true);
if (json_last_error() === JSON_ERROR_NONE) {
$data = $decoded;
}
}
// Форматируем JSON красиво для логов
$prettyJson = is_array($data)
? json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
: (string) $data;
// Отправляем в Rabbit
$this->logsManager->logInfo(
[
'action' => StrHelper::toUpper($name),
'hash' => $hash,
'message' => $s,
'params' => $prettyJson,
],
OrderJobNameEnum::ORDER_SERVICE_LOG
);
$this->logger->error(
join(' ', [
StrHelper::toUpper($name),
'[' . $hash . ']',
$s . ':',
(is_array($data) ? json_encode($data) : $data),
])
);
}
public function setAsConsole(bool $asConsole): OrderService
{
$this->asConsole = $asConsole;
return $this;
}
/**
* @param string|null $token
* @return array|null
* @throws Exception
*/
public function getOrders(?string $token = null): ?array
{
$token = $token ?? $this->token;
return $this->buyOrderRepository->getBuyOrders(['token' => $token]);
}
/**
* @param ?string $token
* @return ?array
* @throws Exception
*/
public function getListOrder(?string $token): ?array
{
$token = $token ?? $this->token;
$list = [];
foreach ($this->buyOrderRepository->getBuyOrders(['token' => $token]) as $order) {
$list[] = $this->orderListNormalizer->normalize($order);
}
return $list;
}
/**
* Создание заказа
*
* @param string $token
* @param ?string $name
* @param ?bool $listNormalize
* @return BuyOrder|array
* @throws Exception
*/
public function create(string $token, ?string $name = null, ?bool $listNormalize = false)
{
$order = new BuyOrder();
$order->setCreateDate(new DateTime());
$order->setUpdateDate(new DateTime());
$name = $this->getNameOrder($token, ($name ?: '%orderName%'));
$order->setHash(md5($token . $name . date('YmdHis')));
$order->setStep(1);
$order->setDeliveryType(0);
$order->setDeliveryStatus(false);
$order->setName($name, true);
$order->setStatus(BuyOrderStatusEnum::REQUEST_DELIVERY);
$order->setMirror($this->mirror);
$order->setToken($token);
$order->setLift(2);
$order->setLocale(App::getCurLocale());
$order->setPayType(PaymentTypeEnum::BANK_TRANSFER);
$order->setEmailSend(true);
$order->setAddressEqual(true);
// todo убрать после перехода на заказ 2.0
$country = App::getCountryList()[App::getCurCountry()] ?? ['id' => 40];
/** @var ListCountry $countryEntity */
$countryEntity = $this->em->getReference(ListCountry::class, $country['id']);
$order->setDeliveryCountry($countryEntity);
//проверяем существование пользователя
$user = $this->userRepo->getUserByToken($token);
if ($user) {
$order->setUser($user);
// если сотрудник, то по умолчанию ставим ключ тестового заказа
if (UserHelper::isEmployee($order->getEmail())) {
$order->setMarkerTest(true);
}
}
// ищем адреса пользователя сначала без создания
$address = $this->addressService->getDefaultAddress($order->getToken(), false, $user);
if (! empty($address) && $address->getId() != $country['id']) {
$address = null;
}
// Не нашли адрес, пробуем поискать среди старых заказов
if (!$address) {
/** @var BuyOrder $oldOrder */
$oldOrder = $this->buyOrderRepository->getBuyOrderWithAddress(
$order->getToken(),
true,
App::getCurLocale() == 'it'
);
if ($oldOrder) {
$address = $this->addressService->getAddressFromOrder($oldOrder);
}
}
// если нет адреса, создаем адрес для заказа
if (!$address) {
$address = $this->addressService->create($order->getToken(), $user);
}
$order->setAddressRecipient($address);
// todo временная установка Delivery данных
if ($address->getSex()) {
$order->setSex($address->getSex());
}
if ($address->getName()) {
$order->setClientName($this->addressService->clear($address->getName()));
}
if ($address->getSurname()) {
$order->setClientSurname($this->addressService->clear($address->getSurname()));
}
if ($address->getEmail()) {
$order->setEmail($address->getEmail());
}
if ($address->getZipCode()) {
$order->setDeliveryIndex($address->getZipCode());
}
if ($address->getCity()) {
$order->setDeliveryCity($address->getCity());
}
if ($address->getRegion()) {
$order->setDeliveryRegion($address->getRegion());
}
if ($address->getStreet()) {
$order->setDeliveryAddress($address->getStreet());
}
if ($address->getBuilding()) {
$order->setDeliveryBuilding($address->getBuilding());
}
if ($address->getApartmentOffice()) {
$order->setDeliveryApartmentOffice($address->getApartmentOffice());
}
if ($address->getCompanyName()) {
$order->setCompanyName($this->addressService->clear($address->getCompanyName()));
}
if ($address->getVatType()) {
$order->setVatType($address->getVatType());
}
if ($address->getVatNumber()) {
$order->setVatNumber($address->getVatNumber());
}
if ($address->getTaxNumber()) {
$order->setTaxNumber($address->getTaxNumber());
}
if ($address->getPhoneCode()) {
$order->setDeliveryPhoneCode($address->getPhoneCode());
}
if ($address->getPhone()) {
$order->setDeliveryPhone($address->getPhone());
}
if ($address->getCountry()) {
$order->setDeliveryCountry($address->getCountry());
}
if (!$order->getDeliveryPhoneCode()) {
if (!$address->getCountry()) {
$countryEntity = App::getCurCountryEntity();
$address->setCountry($countryEntity);
}
$order->setDeliveryPhoneCode($address->getCountry()->getPhoneCode());
}
$currency = CookieHelper::get(CookieKeysConstant::CURRENCY);
if (!in_array($currency, LocaleHelper::getCurrencyList($address->getCountry()->getCode()))) {
$currency = LocaleHelper::getCur(['country' => $address->getCountry()->getCode()]);
}
$order->setCurrency($currency);
$order->setMeasure(LocaleHelper::getUserMeasure(['country' => $address->getCountry()->getCode()]));
$order->setVatPercent(OrderHelper::vatPercent($order));
$this->buyOrderRepository->save($order);
$this->log('CREATEORDER', $order->getHash(), OrderHelper::arrayData($order));
return $listNormalize ? $this->orderListNormalizer->normalize($order) : $order;
}
/**
* @throws Exception
*/
public function editName(string $hash, string $name): array
{
return $this->orderListNormalizer->normalize($this->changeName($hash, $name));
}
/**
* Удаление заказа
*
* @param string $hash
* @return array
* @throws Exception
*/
public function delete(string $hash): array
{
$order = $this->getOrderObj($hash, true);
$token = $order->getToken();
//DelOrder
if ($token == $this->token && $order->getStatus() < BuyOrderStatusEnum::PARTIALLY_PAID) {
$this->setStatus($order, BuyOrderStatusEnum::DELETED);
$this->buyOrderRepository->save($order);
if ($order->getNumber()) {
$this->oneCService->delete($order);
}
}
return [
'res' => 'ok',
'count' => count($order->getArticles()),
'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount(UserHelper::getInstance()->getToken()),
'projects' => [$this->getProjectData($order)],
];
}
/**
* @param string $hash
* @param bool $debug - если true, будут выведены и технические данные
* @return array
* @throws NonUniqueResultException
* @throws Exception
*/
public function getHistory(string $hash, ?bool $debug = false): array
{
$order = $this->getOrderObj($hash, false, true);
$order = $this->orderData($order, ['history' => true]);
$history = $this->historyService->buildHistory($order, $debug);
if ($history) {
return $history;
}
return [];
}
/**
* Возвращает объект заказ
*
* @param string $hash
* @param bool $full
* @param bool $oh получать с историей
* @return BuyOrder
* @throws NonUniqueResultException|NoResultException
*/
public function getOrderObj(string $hash, ?bool $full = false, ?bool $oh = false): BuyOrder
{
/** @var BuyOrder $order */
$order = $this->buyOrderRepository->getOrder($hash, $full, $oh);
if (!$order) {
throw new NotFoundHttpException('Order not found. (getOrderObj)');
}
// проверяем
if ($order->getStatus() < BuyOrderStatusEnum::AWAITING_PAYMENT && $order->getStep() != 1) {
$order->setStep(1);
$this->buyOrderRepository->save($order);
}
return $order;
}
/**
* Возвращает нормализованный заказ
*
* @param string $hash
* @param ?bool $full
* @return array
* @throws Exception
*/
public function getOrder(string $hash, ?bool $full = false): array
{
$order = $this->getOrderObj($hash);
$token = $order->getToken();
if (
$token == $this->token
&& $order->getAddressRecipient()
&& $order->getAddressRecipient()->getCountry() == null
) {
$address = $this->addressService->getDefaultAddress($token);
$order->setStep(1);
$order->setAddressRecipient($address);
}
// если плательщик еще не назначен, надо взять его из адреса доставки
if ($order->getAddressRecipient() && $order->getAddressPayer() == null) {
$order->setAddressPayer($order->getAddressRecipient());
}
//проверим актуальность предложения
$this->checkingOffer($order);
$this->buyOrderRepository->save($order);
return $this->orderData($order, ['full' => $full]);
}
/**
* Формирует массив данных заказа для "ReactJS"
*
* @param BuyOrder $buyOrder
* @param ?array $context
* @param ?array $errors
* @return array
* @throws Exception
*/
public function orderData(BuyOrder $buyOrder, ?array $context = [], ?array $errors = []): array
{
$orderData = $this->orderFiller->orderData(
$buyOrder,
$this->token,
$this->isAccessAdminOrder($buyOrder),
$this->getPickupWarehouses(),
$context,
$errors
);
$orderData['history'] = $this->historyService->buildHistory($orderData);
$managerLogin = $buyOrder->getBuyOrderManager() ? $buyOrder->getManagerLogin() : null;
$orderData['consultants'] = $consultants = $this->portalService->getConsultData(
App::getCurLocale(),
App::getCurCountry(),
UserHelper::getInstance()->getToken(),
$managerLogin
);
$orderData['consultant'] = $managerLogin
? (
!empty($consultants[$managerLogin])
? $consultants[$managerLogin]
: [
'scheduleTime' => [],
'email' => $buyOrder->getBuyOrderManager()->getManagerEmail(),
'phone' => $buyOrder->getBuyOrderManager()->getManagerPhone(),
'daysOfWeek' => [],
'userNickName' => $buyOrder->getBuyOrderManager()->getManagerName(),
'imagePath' => '',
]
)
: null;
if (!empty($orderData['consultant'])) {
$orderData['consultants'] = [
$managerLogin => $orderData['consultant'],
];
}
return $orderData;
}
/**
* Меняет имя заказа
* @throws Exception
*/
public function changeName(string $hash, string $name): BuyOrder
{
$order = $this->getOrderObj($hash);
return $this->changeNameByOrder($order, $name);
}
public function changeNameByOrder(BuyOrder $order, string $name): BuyOrder
{
if ($order->getToken() == UserHelper::getInstance()->getToken()) {
$name = $this->getNameOrder($order->getToken(), strip_tags($name), $order->getId());
$order->setName($name);
$this->buyOrderRepository->save($order);
}
return $order;
}
/**
* Изменение данных заказа
*
* @param string $hash - идентификатор заказа
* @param string $json - данные по заказу
* @return null|BuyOrder
* @throws Exception
*/
public function change(string $hash, string $json): ?BuyOrder
{
try {
$order = $this->getOrderObj($hash);
} catch (NotFoundHttpException | NoResultException $e) {
return null;
}
return $this->changeByOrder($order, $json);
}
/**
* Изменение данных заказа
*
* @param BuyOrder $order - заказ
* @param string $json - данные по заказу
* @return null|BuyOrder
* @throws Exception
*/
public function changeByOrder(BuyOrder $order, string $json): ?BuyOrder
{
$json = trim($json);
// добавим памяти для больших заказов
ini_set('memory_limit', '4096M');
$this->log('CHANGEORDER', $order->getHash(), $json);
$data = json_decode($json, true);
if (!$this->orderAvailableToChange($data, $order)) {
return null;
}
if (isset($data['payment']['type'])) {
$data['payType'] ??= $data['payment']['type'];
}
if ($order->getStatus() >= BuyOrderStatusEnum::PARTIALLY_PAID) {
if (
$order->getStatus() == BuyOrderStatusEnum::PARTIALLY_PAID
&& $data['payType'] != $order->getPayType()
&& $this->getOrderBalance($order) != 0
) {
$order->setPayType($data['payType']);
$this->buyOrderRepository->save($order);
return $order;
}
$this->log('CANT_CHANGE_ORDER_FROM_FRONT_IF_ITS_PAYED', $order->getHash(), $json);
return null;
}
if (
in_array(
($data['recipient']['country']['id'] ?? 0),
[
CountryNumberEnum::DENMARK,
CountryNumberEnum::NORWAY,
CountryNumberEnum::SWITZERLAND,
CountryNumberEnum::SWEDEN,
CountryNumberEnum::HONG_KONG,
CountryNumberEnum::SINGAPORE,
]
)
&& $order->getPayType() === PaymentTypeEnum::BANK_TRANSFER
&& (
empty($data['payment']['type'])
|| (int)$data['payment']['type'] === PaymentTypeEnum::BANK_TRANSFER
)
) {
$data['payment']['type'] = PaymentTypeEnum::VISA_MASTERCARD;
}
//ToDo: Восстановить проверку налогового кода италии после того как будет добавлена соответствующая проверка на фронтенде
//Проверка налогового кода италии, на случай если удалось обойти проверку на UI-е
// if ($data['delivery']['country'] == CountryNumberEnum::ITALY && ! empty($data['taxNumber'])) {
// if (! $this->italianTaxCodeService->isValidTaxCode($data['taxNumber'])) {
// $errors = json_encode($this->italianTaxCodeService->getErrors());
// $this->setError($errors);
// $this->log(
// 'ITALIAN_TAX_CODE_IS_NOT_VALID',
// $order->getHash(),
// $errors
// );
// return $order;
// }
// }
if (
isset($data['delivery']['country'])
&& (int) $data['delivery']['country'] === CountryNumberEnum::ES
&& !empty($data['taxNumber'])
&& !$this->isValidTaxNumber($order, $data)
) {
return $order;
}
if (
$order->getToken() != $this->token
&& !$this->adminChangeMode()
&& (empty($data['checkHash'])
|| !OrderHelper::checkMarker($order->getHash(), $data['checkHash'])
)
) {
return null;
}
$this->cloneOrderIfNeeded($order);
$data = $this->oneCService->normalizeData($data);
// сначала задаем адрес доставки так как дальше плясать от страны надо
if (!empty($data['recipient'])) {
$currentCountryId = ($order->getAddressRecipient()
&& $order->getAddressRecipient()->getCountry()
&& $order->getAddressRecipient()->getCountry()->getId()
)
? $order->getAddressRecipient()->getCountry()->getId()
: null;
if (
// todo временная залипуха для перехода на новую сущность адресов
$order->getDeliveryIndex()
&& $order->getDeliveryCity()
&& $order->getDeliveryAddress()
&& $order->getDeliveryBuilding()
&& $order->getDeliveryPhone()
&& $order->getSex()
&& !$order->getAddressRecipient()
) {
$orderAddress = $this->addressService->getAddressFromOrder($order);
} else {
if (
// todo временно для старого заказа
$order->getAddressRecipient()
&& isset($data['recipient']['id'])
&& $data['recipient']['id'] === 0
) {
$orderAddress = $this->addressService->create($order->getToken());
}
$orderAddress = $this->addressService->setData(
$data['recipient'],
$order->getAddressRecipient(),
$order->getToken()
);
}
$countryId = $data['recipient']['country']['id'] ?? null;
if ($countryId && $currentCountryId && $countryId != $currentCountryId) {
$order->setVatPercent(null);
}
if (
isset($data['recipient']['vatType'])
&& $data['recipient']['vatType'] != $order->getVatType()
) {
$order->setVatType($data['recipient']['vatType']);
}
$order->setAddressRecipient($orderAddress);
// если гипотетически сменилась страна, прогоним через проверку
OrderHelper::params($order);
$this->buyOrderRepository->save($order);
}
if (isset($data['addressEqual']) && $order->getAddressEqual() != $data['addressEqual']) {
$order->setAddressEqual($data['addressEqual']);
}
if (empty($data['addressEqual']) && !empty($data['payer'])) {
$addressPayer = null;
// если клиент выбрал разные адреса плательщика и получателя и в заказе был
// один и тот же адрес, то надо создать новый для изменения
if (
!$order->getAddressEqual()
&& $order->getAddressPayer()
&& $order->getAddressPayer()->getId() != $order->getAddressRecipient()->getId()
) {
$addressPayer = $order->getAddressPayer();
}
if ($data['recipient']['id'] == $data['payer']['id']) {
unset($data['payer']['id']);
}
$data['payer']['addressType'] = OrderAddress::IS_PAYER;
$orderAddress = $this->addressService->setData(
$data['payer'],
$addressPayer,
$order->getToken()
);
if (! $orderAddress->getEmail()) {
$orderAddress->setEmail($order->getEmail());
}
if (! $orderAddress->getEmail()) {
$orderAddress->setEmail($order->getEmail());
}
$order->setAddressPayer($orderAddress);
} else {
$order->setAddressPayer($order->getAddressRecipient());
}
$this->buyOrderRepository->save($order);
$order->setVatPercent(OrderHelper::vatPercent($order));
$this->orderFiller->fillWarehouseData($order, $data);
if (
isset($data['confirmed'])
&& $order->getEmail() != 'r.zagorudko@tile.expert'
&& $order->getEmail() != 'zaromeo@yandex.ru'
) {
$this->setNewTermsOfDelivery($order, (bool) $data['confirmed']);
}
if (isset($data['step']) && in_array($data['step'], [1, 2, 3, 4])) {
if ($order->getStatus() < BuyOrderStatusEnum::PARTIALLY_PAID) {
$order->setStep($data['step']);
} else {
$order->setStep(3);
}
}
if (
isset($data['payment']['type'])
&& $data['payment']['type'] != $order->getPayType()
&& PaymentTypeEnum::isAvailableMethod((int) $data['payment']['type'])
) {
if (
OrderPaymentHelper::orderCountryWithoutInvoiceAsPaymentType($order)
&& $data['payment']['type'] == PaymentTypeEnum::BANK_TRANSFER
) {
$data['payment']['type'] = PaymentTypeEnum::VISA_MASTERCARD;
}
$order->setPayType(PaymentTypeEnum::changeOldPayPalToNew((int) $data['payment']['type']));
}
$order->setPersonalDataAgree(!empty($data['personalDataAgree']));
if (isset($data['markerTest']) && $order->getToken() == $this->token) {
$order->setMarkerTest(!empty($data['markerTest']));
}
if (!empty($data['name'])) {
$name = $this->getNameOrder($order->getToken(), strip_tags($data['name']), $order->getId());
$order->setName($name, true);
}
if (!empty($data['sex'])) {
$order->setSex(SexEnum::getFemaleIfNotMale((int) $data['sex']));
}
$order->setTaxNumber($data['taxNumber'] ?? $order->getTaxNumber());
if (!empty($data['clientName'])) {
$order->setClientName($this->addressService->clear($data['clientName']));
}
if (!empty($data['clientSurname'])) {
$order->setClientSurname($this->addressService->clear($data['clientSurname']));
}
if (!empty($data['description'])) {
$order->setDescription($this->addressService->clear($data['description']));
}
if (!empty($data['carrierName'])) {
$order->setCarrierName($this->addressService->clear($data['carrierName']));
}
if (!empty($data['delivery'])) {
$this->orderFiller->fillOrderDeliveryFromFrontData($data['delivery'], $order, $this->oldOrder);
}
if (!empty($data['payType']) && $order->getStatus() < BuyOrderStatusEnum::PAID_NOT_CONFIRMED) {
$type = PaymentTypeEnum::getBankTransferTypeIfPayTypeNotAvailable($data['payType']);
$type = PaymentTypeEnum::changeOldPayPalToNew($type);
if (
OrderPaymentHelper::orderCountryWithoutInvoiceAsPaymentType($order)
&& $type === PaymentTypeEnum::BANK_TRANSFER
) {
$type = PaymentTypeEnum::VISA_MASTERCARD;
}
$order->setPayType($type);
}
$this->orderFiller->fillOrderVatFromFrontData($data['vat'] ?? [], $order);
$this->orderFiller->fillCompanyNameForOrder($order, ($data['companyName'] ?? null) ?: null);
if (
!empty($data['email'])
&& empty($data['checkHash']) // 1С не меняет мэйлы в заказе, а значит и валидность не проверяем
) {
$checkEmail = $this->userService->checkEmail($data['email']);
if ($checkEmail->getStatus()) {
$order->setEmail($data['email']);
} else {
$msg = $this->translationService->trans(
'buyOrder.fields.error.email_exist',
App::getCurLocale(),
[
'%login%' => App::generateUrl('app_login', ['_locale' => App::getCurLocale(true)]),
]
);
$this->errors[] = $msg;
$this->log('CHECKEMAIL', $order->getHash(), $msg);
}
}
// должна быть после recipient, но до items чтобы правильно просчитать цены
$this->localization($order, $data);
// todo временная команда создания нового адреса по заказу если его нет
if ($order->getAddressRecipient() == null/* && $order->getDeliveryIndex() != null*/) {
$addressRecipient = $this->addressService->getAddressFromOrder($order);
$order->setAddressRecipient($addressRecipient);
}
$data = $this->updateItemsForOrder($data, $order, $this->oldOrder, false);
// todo ================================================================================================================
$markerResetOfOldOrder = OrderHelper::markerReset($this->oldOrder, false);
$markerResetOfNewOrder = OrderHelper::markerReset($order, false);
// если что-то изменилось с фронта - то поставим статус заказа в По запросу
if (
$order->getStatus() < BuyOrderStatusEnum::PARTIALLY_PAID
&& md5($markerResetOfOldOrder) != md5($markerResetOfNewOrder)
) {
$this->setStatus($order, BuyOrderStatusEnum::REQUEST_DELIVERY);
}
// заказ сбрасываем только если он не оплачен, уже отправлялся в 1С и данные изменились
if (
$order->getStatus() < BuyOrderStatusEnum::PARTIALLY_PAID
&& ($order->getSendCreate1C()
&& md5($markerResetOfOldOrder) != md5($markerResetOfNewOrder)
|| !empty($data['reset'])
)
) {
$info = [
'old' => $markerResetOfOldOrder,
'now' => $markerResetOfNewOrder,
];
$order->setBankTransferRequested(false);
$this->reset(
$order,
ReasonForResetOrderEnum::CHANGED_ITEMS_OR_INDEX_OR_COUNTRY(),
'Changed items or index or country "' . json_encode($info, JSON_UNESCAPED_UNICODE)
);
}
if (isset($data['confirmed'])) {
// фиксируем на всякий случай, что новые сроки подтвержденные
$this->setNewTermsOfDelivery($order, (bool) $data['confirmed']);
}
$this->buyOrderRepository->save($order);
$data = $this->sendOrderTo1CIfItReady($order, $data);
$this->buyOrderRepository->save($order);
return $this->changeOrderFrom1C($order, $data);
}
public function changeOrderByHashFrom1CWithJsonAsString(string $hash, string $json): ?BuyOrder
{
try {
$order = $this->getOrderObj($hash);
} catch (NotFoundHttpException | NoResultException | NonUniqueResultException $e) {
$this->log('ERROR_CHANGEORDER_BYHASH_FROM1C_WITHJSONASSTRING', $e->getMessage(), $json);
return null;
}
$json = trim($json);
$data = json_decode($json, true);
$response = $this->changeOrderFrom1C($order, $data);
if ($this->needRunScenario && isset($data['status'])) {
$scenario = new ChangeOrderStatusScenario(
$order->getStatus(),
$this->oldOrder->getStatus(),
true,
[],
$order,
$this->oldOrder,
$this->historyService,
$this->oneCService
);
$scenario->run();
$this->needRunScenario = false;
}
return $response;
}
public function changeOrderFrom1CWithJsonAsString(BuyOrder $order, string $json): ?BuyOrder
{
$json = trim($json);
$data = json_decode($json, true);
return $this->changeOrderFrom1C($order, $data);
}
/**
* Изменение данных заказа из 1С - включая синхронизации и обновление всех данных заказа
*
* @param BuyOrder $order - заказ
* @param array $data - данные по заказу
* @return null|BuyOrder
* @throws Exception
*/
public function changeOrderFrom1C(BuyOrder $order, array $data): ?BuyOrder
{
// добавим памяти для больших заказов
ini_set('memory_limit', '4096M');
$this->log('CHANGEORDERFROM1C', $order->getHash(), json_encode($data));
if (
!empty($data['checkHash'])
&& !OrderHelper::checkMarker($order->getHash(), $data['checkHash'])
) {
$this->log('ERROR_CHANGE_ORDER_FROM_1C', $order->getHash(), 'Incorrect marker');
return null;
}
$this->buyOrderRepository->refresh($order);
if (!$this->orderAvailableToChange($data, $order)) {
return null;
}
$this->cloneOrderIfNeeded($order);
$data = $this->oneCService->normalizeData($data);
$this->orderFiller->fillHolidaysForItems($order, $data);
if (isset($data['step']) && in_array($data['step'], [1, 2, 3])) {
if ($order->getStatus() < BuyOrderStatusEnum::PARTIALLY_PAID) {
$order->setStep($data['step']);
} else {
$order->setStep(3);
}
}
if (
isset($data['payment']['type'])
&& $data['payment']['type'] != $order->getPayType()
&& PaymentTypeEnum::isAvailableMethod((int) $data['payment']['type'])
) {
if (
OrderPaymentHelper::orderCountryWithoutInvoiceAsPaymentType($order)
&& $data['payment']['type'] == PaymentTypeEnum::BANK_TRANSFER
) {
$data['payment']['type'] = PaymentTypeEnum::VISA_MASTERCARD;
}
$order->setPayType(PaymentTypeEnum::changeOldPayPalToNew((int) $data['payment']['type']));
}
if (!empty($data['name'])) {
$name = $this->getNameOrder($order->getToken(), strip_tags($data['name']), $order->getId());
$order->setName($name, true);
}
$order->setTaxNumber($data['taxNumber'] ?? $order->getTaxNumber());
if (!empty($data['clientName'])) {
$order->setClientName($this->addressService->clear($data['clientName']));
}
if (!empty($data['clientSurname'])) {
$order->setClientSurname($this->addressService->clear($data['clientSurname']));
}
if (!empty($data['description'])) {
$order->setDescription($this->addressService->clear($data['description']));
}
if (!empty($data['carrierName'])) {
$order->setCarrierName($this->addressService->clear($data['carrierName']));
}
if (!empty($data['payType']) && $order->getStatus() < BuyOrderStatusEnum::PAID_NOT_CONFIRMED) {
$type = PaymentTypeEnum::getBankTransferTypeIfPayTypeNotAvailable($data['payType']);
$type = PaymentTypeEnum::changeOldPayPalToNew($type);
if (
OrderPaymentHelper::orderCountryWithoutInvoiceAsPaymentType($order)
&& $type === PaymentTypeEnum::BANK_TRANSFER
) {
$type = PaymentTypeEnum::VISA_MASTERCARD;
}
$order->setPayType($type);
}
// ставим спец цену в заказе запрещающую изменения цен
if (isset($data['specPrice'])) {
$order->setSpecPrice(!empty($data['specPrice']));
try {
$data = $this->updateItemsForOrder($data, $order, $this->oldOrder, !empty($data['checkHash']));
} catch (Exception $e) {
$this->log('ERROR_CHANGE_ORDER_ITEMS_FROM_1C', $order->getHash(), 'Error for updated items: ' . $e->getMessage());
return null;
}
}
if (isset($data['confirmed'])) {
// фиксируем на всякий случай, что новые сроки подтвержденные
$this->setNewTermsOfDelivery($order, (bool) $data['confirmed']);
}
if (isset($data['feedback']) && is_scalar($data['feedback'])) {
$feedback = is_string($data['feedback'])
? (strcasecmp('true', $data['feedback']) === 0)
: (bool) $data['feedback'];
$order->setProhibitSendReviewRequest(!$feedback);
}
$this->checkSumOrSumBackData($data, $order);
if (isset($data['dateCalc'])) {
// отправка письма 20 должна быть только если заказ ещё не оплачен,
// а так же это не из сетПэй
$send20 = empty($data['checkHash'])
&& !empty($data['status']) //Добавил "!" для исправления ошибки, нужна дополнительная проверка по логике
&& !in_array($data['status'], [BuyOrderStatusEnum::PARTIALLY_PAID, BuyOrderStatusEnum::PAID]);
$order = $this->offsetDate($data, $order, $send20);
}
$params = $this->setDataFromResponse($order, $data);
// создавать тему на портале нужно когда известен номер заказа
if ($this->portalService->themePortal($order, $this->oldOrder)) {
$this->buyOrderRepository->save($order);
}
if (!empty($data['sendReview'])) {
// отправляем приглашение на отзыв если был добавлен ключ для отправки
if (
App::getParameter('order_send_review')
&& $this->oldOrder->getSendReview() != $order->getSendReview()
&& $order->getSendReview() == 1
) {
$this->historyService->saveHistoryAndSendEmail(
$order,
'order.review',
App::getParameter('order_send_review')
);
}
}
if (isset($data['stage']) && $order->getEmail()) {
$stageAlias = $data['stage'] == 3 ? 'delayed_delivery_4' : 'delivery_delayed_3';
$this->historyService->saveHistoryAndSendEmail(
$order,
$stageAlias,
OrderHistoryService::SEND_EMAIL,
$params
);
}
if (!empty($data['confirmDateOffset'])) {
$order->setOffsetDate(DateHelper::standard($data['confirmDateOffset']));
$this->historyService->saveHistoryAndSendEmail(
$order,
'delay_confirmation',
OrderHistoryService::SEND_EMAIL
);
}
// записываем новые хеши данных заказа
// хеш нужно обязательно перестраивать перед обновлением иначе будут дубль запросы в 1С
$this->setNewHashOrder($order);
// меняем дату обновления только если заказ обновлялся не консольно, то есть не роботом
// это нужно чтобы мертвый заказ со временем перестал провериться на актуальность,
// если пользователь в него не входил
if (!$this->asConsole) {
$order->setUpdateDate(new DateTime());
}
$this->buyOrderRepository->save($order);
return $order;
}
/**
* Сброс и отправка заказа в 1С
*/
public function resetAndSendOrderTo1C(string $hash): bool
{
if (!$this->asConsole) {
echo 'Only console.' . PHP_EOL;
return false;
}
try {
$order = $this->getOrderObj($hash);
} catch (NotFoundHttpException | NonUniqueResultException $e) {
echo 'Order not found.' . PHP_EOL;
return false;
}
if ($order->getStatus() > BuyOrderStatusEnum::AWAITING_PAYMENT) {
echo 'Order status more than 3.' . PHP_EOL;
return false;
}
if (!$order->getSendCreate1C()) {
echo 'Order not sent to 1C yet.' . PHP_EOL;
return false;
}
$this->reset($order, ReasonForResetOrderEnum::RESEND_ADMIN(), 'Resend order by admin command');
if (!OrderHelper::readyCreate($order)) {
echo 'Order doesnt ready to create.' . PHP_EOL;
return false;
}
$this->oneCService->createOrderOneC(App::getRequest(), $order);
// создавать тему на портале нужно когда известен номер заказа
$this->portalService->themePortal($order, $order);
$this->setNewHashOrder($order);
$this->buyOrderRepository->save($order);
return true;
}
/**
* Перемещение или копирование заказа
* @param array $data
* @return array
* @throws Exception
*/
public function transferItems(array $data): array
{
if (empty($data)) {
return ['err' => []];
}
$error = [];
if (empty($data['ides']) || count($data['ides']) == 0) {
$error[] = 'ides is empty';
}
if (empty($data['hashSource'])) {
$error[] = 'hashSource is empty';
}
if (empty($data['hashReceiver'])) {
$error[] = 'hashReceiver is empty';
}
if (count($error) > 0) {
return ['err' => $error];
}
$orderSource = $this->getOrderObj($data['hashSource']);
/** @var BuyOrderArticle $item */
foreach ($orderSource->getArticles() as $item) {
if (in_array($item->getId(), $data['ides'])) {
// если получатель массив тогда копируем
if (is_array($data['hashReceiver'])) {
foreach ($data['hashReceiver'] as $hashReceiver) {
$orderReceiver = $this->getOrderObj($hashReceiver);
if ($findItem = $this->findItemInOrder($orderReceiver, $item)) {
$this->itemService->setItemData(
$orderReceiver,
$findItem,
$item->getAmount(),
$item->getType()
);
$this->em->persist($findItem);
} else {
$this->itemService->createCopyItem($item, $orderReceiver);
}
$this->em->persist($orderReceiver);
$this->oneCService->sendOrResendOrderToOneC($orderReceiver);
}
$this->em->flush();
} else { // если строка значит перемещаем
$orderReceiver = $this->getOrderObj($data['hashReceiver']);
// проверяем существует ли в данном заказе уже этот артикул, если нет, перемещаем
if ($this->findItemInOrder($orderReceiver, $item) == null) {
$changeItem = false;
if ($item->getCurrency() != $orderReceiver->getCurrency()) {
$item->setCurrency($orderReceiver->getCurrency());
$changeItem = true;
}
if ($item->getMeasure() != $orderReceiver->getMeasure()) {
$item->setMeasure($orderReceiver->getMeasure());
$changeItem = true;
}
if ($changeItem) {
$this->itemService->setItemData(
$orderReceiver,
$item,
$item->getAmount(),
$item->getType()
);
}
$item->setBuyOrder($orderReceiver);
$this->em->persist($orderReceiver);
$this->em->persist($item);
$this->em->flush();
$this->oneCService->sendOrResendOrderToOneC($orderReceiver);
}
}
$this->em->persist($orderSource);
$this->em->flush();
$this->oneCService->sendOrResendOrderToOneC($orderSource);
}
}
$order = $this->orderData($orderSource);
return ['orderSource' => $order];
}
/**
* @param BuyOrder $order
* @param BuyOrderArticle $item
* @return null|BuyOrderArticle
*/
private function findItemInOrder(BuyOrder $order, BuyOrderArticle $item): ?BuyOrderArticle
{
foreach ($order->getArticles() as $itemOrder) {
if ($itemOrder->chooseCode() == $item->chooseCode()) {
return $itemOrder;
}
}
return null;
}
/**
* @param BuyOrder $order
* @param array $ides
* @return array
* @throws Exception
*/
public function delItems(BuyOrder $order, array $ides): array
{
$this->log('DELETE_ITEM_ORDER_TO_NEW_FLOW', $order->getHash(), $ides);
$isDel = false;
/** @var BuyOrderArticle $item */
foreach ($order->getArticles() as $item) {
if (in_array($item->getId(), $ides)) {
$isDel = true;
$order->removeArticle($item);
$this->em->remove($item);
}
}
if ($isDel) {
$this->buyOrderRepository->save($order);
$this->reset($order, ReasonForResetOrderEnum::DELETE_ITEM(), 'DELETE ITEMS IN ORDER');
}
return $this->orderData($order);
}
/**
* Удаление артикула из заказа
*
* @param int $itemId
* @param string $orderHash
* @return array|null
* @throws NonUniqueResultException
* @throws NoResultException
*/
public function deleteArticleFromOrder(
int $itemId,
string $orderHash
): ?array {
// заказ можно менять только до момента оплаты
$params['status'] = [1, 2, 3];
$params['token'] = $this->token;
$params['hash'] = $orderHash;
$order = $this->buyOrderRepository->getBuyOrder($params);
if (null === $order) {
return null;
}
/** @var Article $item */
$item = $this->articleRepository->getArticle($itemId);
if (null === $item) {
return null;
}
// ищем артикул в заказе
$orderItem = null;
/** @var BuyOrderArticle|null $orderItem */
/** @var BuyOrderArticle $oneArticleInCurrentOrder */
foreach ($order->getArticles() as $key => $oneArticleInCurrentOrder) {
if ($oneArticleInCurrentOrder->getArticle()->getId() === $item->getId()) {
$orderItem = $oneArticleInCurrentOrder;
}
}
if (null === $orderItem) {
return null;
}
$this->itemService->deleteItemFromOrder($order, $orderItem);
// сброс заказа в начальное состояние в 1С
$this->reset($order, ReasonForResetOrderEnum::DELETE_ITEM(), 'DELETE ITEM');
return [
'count' => $order->getArticles()->count(),
'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount($this->token),
'projects' => [$this->getProjectData($order)],
];
}
/**
* Сброс заказа в состояние черновика
*
* @param BuyOrder $order
* @param ReasonForResetOrderEnum $reasonForResetOrder
* @param ?string $info
* @return BuyOrder
* @throws SoapFault
*/
public function reset(
BuyOrder $order,
ReasonForResetOrderEnum $reasonForResetOrder,
?string $info = null
): BuyOrder { // todo после перехода на заказ 2.0 поставить private
//проверяем статус заказа, если больше 3-х, то сбрасывать заказ нельзя
if ($order->getStatus() && $order->getStatus() > BuyOrderStatusEnum::AWAITING_PAYMENT) {
$this->log('RESETORDERSITE', $order->getHash(), $info, 'The order cannot be changed!');
return $order;
}
$orderSendCreate1CYet = $order->getSendCreate1C();
// если заказ рассчитывается вручную и сбрасывается,
// надо установить ключ письма для отложенной отправки письма о сбросе по заказу
if (
$order->getStatus() == BuyOrderStatusEnum::AWAITING_PAYMENT
&& $order->getDeliveryCalculateType() === 0
) {
$order->setKeyEmail('reset_time_change');
}
$this->setStatus($order, BuyOrderStatusEnum::REQUEST_DELIVERY, ['fromResetOrder' => $reasonForResetOrder->isUserDidCancelRequest()]);
if ($order->getSpecPrice()) {
$this->log('RESET SPEC PRICE', $order->getHash(), $info);
$order->setSpecPrice(null);
}
// сохраняем сброс заказа на сайте до отправки в 1С
$this->orderFiller->fillOrderAsDraft($order);
if ($orderSendCreate1CYet) {
$this->log('RESETORDERONEC', $order->getHash(), $info);
$this->oneCService->resetOrderOneC($order, $info, $this->timer);
} else {
$this->log('RESETORDERSITE', $order->getHash(), $info);
}
return $order;
}
/**
* Проверяет включен ли режим редактирования администратора
*
* @return bool
* @throws Exception
*/
private function adminChangeMode(): bool
{
return $this->authorizationChecker->isGranted('ROLE_ADMIN');
}
/**
* Проверяет не консультант это случаем
*
* @return bool
* @throws Exception
*/
private function isConsultant(): bool
{
return $this->authorizationChecker->isGranted('ROLE_CONS');
}
/**
* Запрос ручного расчета доставки и стоимости или ускорения доставки
*
* @param string $hash
* @param ?string $json
* @return array
* @throws Exception
* @internal param BuyOrder $order
*/
public function requestCalculateDeliveryByHash(string $hash, ?string $json = null): ?array
{
try {
$order = $this->getOrderObj($hash);
} catch (NoResultException | NonUniqueResultException $e) {
return null;
}
return $this->requestCalculateDelivery($order, $json);
}
/**
* Запрос ручного расчета доставки и стоимости или ускорения доставки
* @throws Exception
*/
public function requestCalculateDelivery(BuyOrder $order, ?string $json = null): ?array
{
if ($order->getToken() !== $this->token && !$this->adminChangeMode()) {
return null;
}
$result['order'] = $order;
if (!$json && !$order->getSendCreate1C() && $order->getDeliveryIndex()) {
$json = json_encode(['delivery' => ['index' => $order->getDeliveryIndex()]]);
}
$jsonData = null;
if ($order->getUser() === null && App::getCurUser() === null) {
$jsonData = json_decode($json, true);
if (
$order->getEmail() === null
&& isset($jsonData['email'])
&& ! $this->findUserByEmail($jsonData['email'])
) {
$address = $order->getAddressRecipient();
$order->setEmail($jsonData['email']);
$address->setEmail($jsonData['email']);
$order->setAddressRecipient($address);
}
}
// Данные клиента
if ($json) {
$order = $this->changeByOrder($order, $json);
} else {
$jsonData ??= json_decode($json, true);
$this->localization($order, $jsonData);
}
$isReadyOrder = $this->oneCService->readyRequestDelivery($order);
if ($isReadyOrder) {
$result = $this->oneCService->delivery($order);
$order = $this->offsetDate($result, $order);
//Учитывая изменение статуса заказ нужно запускать сценарий
$this->needRunScenario = true;
$this->setStatus($order, BuyOrderStatusEnum::AWAITING_CALCULATION, ['json_data' => $json]);
if (!empty($result['delivery'])) {
$this->setDeliveryOrder($result, $order);
$this->setStatus($order, BuyOrderStatusEnum::AWAITING_PAYMENT);
}
$this->buyOrderRepository->save($order);
return $this->orderData($order);
}
return $this->orderData($order, [], ['not ready order']);
}
/**
* Устанавливает сроки смещения
*
* @param array|null $data
* @param BuyOrder $order
* @param ?bool $send20
* @return BuyOrder
* @throws Exception
*/
public function offsetDate(?array $data, BuyOrder $order, ?bool $send20 = false): BuyOrder
{
if (empty($data['dateCalc'])) {
$dateOffset = new DateTime();
$dateOffset->modify('+2 day');
$order->setOffsetDate($dateOffset);
return $order;
}
// сбрасываем старую причину
$order->setOffsetExplain(null);
$order->setOffsetDate(DateHelper::standard($data['dateCalc']));
if (!empty($data['dateCalcReason']) && in_array($data['dateCalcReason'], [1, 2, 3])) {
$order->setOffsetReason($data['dateCalcReason']);
if ($data['dateCalcReason'] == 3 && !empty($data['explain'])) { // причина указанная БМ-ом
$order->setOffsetExplain($data['explain']);
$alias = $order->getStatus() == BuyOrderStatusEnum::AWAITING_CALCULATION
? 'verification_possibility_deliver_2'
: 'verification_possibility_deliver_1';
} elseif ($data['dateCalcReason'] == 2) { // фабрика уточняет сроки пр-ва
$alias = $order->getStatus() == BuyOrderStatusEnum::AWAITING_CALCULATION
? 'verification_possibility_deliver_2b'
: 'verification_possibility_deliver_1b';
} else { // каникулы на фабрике
$alias = $order->getStatus() == BuyOrderStatusEnum::AWAITING_CALCULATION
? 'verification_possibility_deliver_2a'
: 'verification_possibility_deliver_1a';
}
$this->historyService->saveHistoryAndSendEmail(
$order,
$alias,
OrderHistoryService::SEND_EMAIL
);
} elseif ($send20) {
$this->historyService->saveHistoryAndSendEmail(
$order,
'calculation_shipping_more_time',
OrderHistoryService::SEND_EMAIL
);
}
return $order;
}
/**
* Смена статуса заказа
*
* @param BuyOrder $order
* @param int $status
* @param ?array $params
* @return BuyOrder
* @throws Exception
*/
public function setStatus(BuyOrder $order, int $status, ?array $params = []): BuyOrder
{
// защита от несанкционированной смены статуса заказа в черновик
if ($this->isForbiddenStatusChange($status, $order)) {
$this->needRunScenario = false;
return $order;
}
$oldStatus = $this->oldOrder ? $this->oldOrder->getStatus() : 0;
$this->cloneOrderIfNeeded($order);
$sendEmail = !($params['syncFromOneC'] ?? false);
// проверка дубль заказов
$this->addressService->mergeAddresses($order);
if ($oldStatus === $status && $status === BuyOrderStatusEnum::PAID_NOT_CONFIRMED) {
$this->historyService->saveHistoryAndSendEmail($order, 'reminder_acceptance_period', $sendEmail);
$this->needRunScenario = false;
return $order;
}
if ($this->dontChangeOrderStatus(isset($params['fileBase64']), $oldStatus, $status)) {
$this->needRunScenario = false;
return $order;
}
if ($this->needRunScenario) {
$scenario = new ChangeOrderStatusScenario(
$status,
$oldStatus,
$sendEmail,
$params,
$order,
$this->oldOrder,
$this->historyService,
$this->oneCService
);
$scenario->run();
$this->needRunScenario = false;
}
return $order;
}
/**
* Устанавливает сроки заказ
*
* @param array $data
* @param BuyOrder $order
* @throws Exception
*/
public function setDeliveryOrder(array $data, BuyOrder $order)
{
if (!empty($data['delivery'])) {
$this->log('DELIVERY DATA', $order->getHash(), json_encode($data['delivery']));
$oldDate = $order->getDeliveryTimeMax() ? $order->getDeliveryTimeMax()->getTimestamp() : 0;
$oldTimeDelivery = OrderHelper::time($order, true);
$delivery = $data['delivery'];
if (isset($delivery['type'])) {
if ($delivery['type'] == 1) {
$order->setDeliveryType(1);
} else {
$order->setDeliveryType(0);
}
}
if (!empty($delivery['price'])) {
$price = PriceHelper::formatPrice($delivery['price']);
$order->setDelivery($price);
if (is_numeric($price) && $price != 0) {
$order->setStatus(BuyOrderStatusEnum::AWAITING_PAYMENT);
}
} elseif (!empty($delivery['status'])) {
$order->setDelivery(0);
}
if (!empty($delivery['priceEuro'])) {
$price = PriceHelper::formatPrice($delivery['priceEuro']);
$order->setDeliveryPriceEuro($price);
} elseif (!empty($delivery['status'])) {
//если 1С передало статус 3, то считаем, что доставка рассчитана, но бесплатна
$order->setDeliveryPriceEuro(0);
}
if (isset($delivery['lift'])) {
if (BuyOrderLiftEnum::isValid((int) $delivery['lift'])) {
$order->setLift((int) $delivery['lift']);
} else {
$order->setLift(BuyOrderLiftEnum::UNDEFINED);
}
}
if (!empty($delivery['weightReport'])) {
$weightReport = (float) str_replace(',', '.', $delivery['weightReport']);
$order->setWeightReport($weightReport);
} elseif (isset($delivery['weightReport'])) {
$order->setWeightReport(null);
}
OrderHelper::updateWeightGrossForOrderInNeeded($order, $delivery['weightGross'] ?? null);
if (!empty($delivery['dateExecution'])) {
// проверяем дату на минуты
$dateExecution = DateHelper::standard($delivery['dateExecution']);
$order->setPayDateExecution($dateExecution);
} elseif (isset($delivery['dateExecution'])) {
$order->setPayDateExecution(null);
}
if (!empty($delivery['timeMin'])) {
if ($timeMin = DateHelper::standard($delivery['timeMin'])) {
if (
$order->getDeliveryTimeMin() != null &&
(
$order->getDeliveryOldTimeMin() == null ||
$order->getDeliveryTimeMin()->getTimestamp() != $order->getDeliveryTimeMin()->getTimestamp()
)
) {
$order->setDeliveryOldTimeMin($order->getDeliveryTimeMin());
}
$order->setDeliveryTimeMin($timeMin);
}
} elseif (isset($delivery['timeMin'])) {
$order->setDeliveryTimeMin(null);
}
if (!empty($delivery['timeMax'])) {
if ($timeMax = DateHelper::standard($delivery['timeMax'])) {
if (
$order->getDeliveryTimeMax() != null
&& ($order->getDeliveryOldTimeMax() == null
|| $order->getDeliveryTimeMax()->getTimestamp() != $order->getDeliveryTimeMax()->getTimestamp()
)
) {
$order->setDeliveryOldTimeMax($order->getDeliveryTimeMax());
}
$order->setDeliveryTimeMax($timeMax);
}
} elseif (isset($delivery['timeMax'])) {
$order->setDeliveryTimeMax(null);
}
if (!empty($delivery['timeFastPayMin'])) {
if ($timeFastPayMin = DateHelper::standard($delivery['timeFastPayMin'])) {
$order->setDeliveryTimeFastPayMin($timeFastPayMin);
}
} elseif (isset($delivery['timeFastPayMin'])) {
$order->setDeliveryTimeFastPayMin(null);
}
if (!empty($delivery['timeFastPayMax'])) {
if ($timeFastPayMax = DateHelper::standard($delivery['timeFastPayMax'])) {
$order->setDeliveryTimeFastPayMax($timeFastPayMax);
}
} elseif (isset($delivery['timeFastPayMax'])) {
$order->setDeliveryTimeFastPayMax(null);
}
$timeConfirm = false;
if (!empty($delivery['timeConfirmMin'])) {
// если текущих сроков нет, то берем старые
if ($order->getDeliveryTimeMin() == null && $order->getDeliveryOldTimeMin()) {
$order->setDeliveryTimeMin($order->getDeliveryOldTimeMin());
}
if ($timeConfirmMin = DateHelper::standard($delivery['timeConfirmMin'])) {
$order->setDeliveryTimeConfirmMin($timeConfirmMin);
$timeConfirm = true;
}
} elseif (isset($delivery['timeConfirmMin'])) {
$order->setDeliveryTimeConfirmMin(null);
}
if (!empty($delivery['timeConfirmMax'])) {
// если текущих сроков нет, то берем старые
if ($order->getDeliveryTimeMax() == null && $order->getDeliveryOldTimeMax()) {
$order->setDeliveryTimeMax($order->getDeliveryOldTimeMax());
}
if ($timeConfirmMax = DateHelper::standard($delivery['timeConfirmMax'])) {
$order->setDeliveryTimeConfirmMax($timeConfirmMax);
$timeConfirm = true;
}
} elseif (isset($delivery['timeConfirmMax'])) {
$order->setDeliveryTimeConfirmMax(null);
}
// дополнительная проверка для стоимости доставки
if ($order->getDeliveryTimeMin() === null && $order->getDelivery()) {
$order->setDelivery(null);
}
if (!empty($data['items'])) {
$artTimeTypes = [];
/** @var BuyOrderArticle $item */
foreach ($order->getArticles() as $item) {
if (isset($data['items'][$item->chooseCode()])) {
if (
!empty($data['items'][$item->chooseCode()]['holiday']) &&
(
$data['items'][$item->chooseCode()]['holiday'] == 'true' ||
$data['items'][$item->chooseCode()]['holiday'] === true
)
) {
$item->setHoliday(true);
} else {
$item->setHoliday(false);
}
if (!empty($data['items'][$item->chooseCode()]['timeMin'])) {
if ($timeMin = DateHelper::standard($data['items'][$item->chooseCode()]['timeMin'])) {
$item->setDeliveryTimeMin($timeMin);
}
} elseif (isset($data['items'][$item->chooseCode()]['timeMin'])) {
$item->setDeliveryTimeMin(null);
}
if (!empty($data['items'][$item->chooseCode()]['timeMax'])) {
if ($timeMax = DateHelper::standard($data['items'][$item->chooseCode()]['timeMax'])) {
$item->setDeliveryTimeMax($timeMax);
}
} elseif (isset($data['items'][$item->chooseCode()]['timeMax'])) {
$item->setDeliveryTimeMax(null);
}
if (!empty($data['items'][$item->chooseCode()]['timeFastPayMin'])) {
if (
$timeFastPayMin = DateHelper::standard(
$data['items'][$item->chooseCode()]['timeFastPayMin']
)
) {
$item->setDeliveryTimeMinFaster($timeFastPayMin);
}
} elseif (isset($data['items'][$item->chooseCode()]['timeFastPayMin'])) {
$item->setDeliveryTimeMinFaster(null);
}
if (!empty($data['items'][$item->chooseCode()]['timeFastPayMax'])) {
if (
$timeFastPayMax = DateHelper::standard(
$data['items'][$item->chooseCode()]['timeFastPayMax']
)
) {
$item->setDeliveryTimeMaxFaster($timeFastPayMax);
}
} elseif (isset($data['items'][$item->chooseCode()]['timeMax'])) {
$item->setDeliveryTimeMaxFaster(null);
}
if (!empty($data['items'][$item->chooseCode()]['timeConfirmMin'])) {
// если текущих сроков нет, то берем старые
if ($item->getDeliveryTimeMin() == null && $order->getDeliveryOldTimeMin()) {
$item->setDeliveryTimeMin($order->getDeliveryOldTimeMin());
}
if (
$timeMin = DateHelper::standard(
$data['items'][$item->chooseCode()]['timeConfirmMin']
)
) {
$item->setDeliveryTimeConfirmMin($timeMin);
// на случай если сброшены обычные сроки берем их из заказа
if (!$item->getDeliveryTimeMin() && $order->getDeliveryTimeMin()) {
$item->setDeliveryTimeMin($order->getDeliveryTimeMin());
}
}
} elseif (isset($data['items'][$item->chooseCode()]['timeConfirmMin'])) {
$item->setDeliveryTimeConfirmMin(null);
}
if (!empty($data['items'][$item->chooseCode()]['timeConfirmMax'])) {
// если текущих сроков нет, то берем старые
if ($item->getDeliveryTimeMax() == null && $order->getDeliveryOldTimeMax()) {
$item->setDeliveryTimeMax($order->getDeliveryOldTimeMax());
}
if (
$timeMax = DateHelper::standard(
$data['items'][$item->chooseCode()]['timeConfirmMax']
)
) {
$item->setDeliveryTimeConfirmMax($timeMax);
// на случай если сброшены обычные сроки берем их из заказа
if (!$item->getDeliveryTimeMax() && $order->getDeliveryTimeMax()) {
$item->setDeliveryTimeMax($order->getDeliveryTimeMax());
}
}
} elseif (isset($data['items'][$item->chooseCode()]['timeConfirmMax'])) {
$item->setDeliveryTimeConfirmMax(null);
}
if (!empty($data['items'][$item->chooseCode()]['timeType'])) {
$itemTimeType = (int) $data['items'][$item->chooseCode()]['timeType'];
// костыль так как 1С неправильно присылает тип срока если ранее он был рассчитан
$itemTimeType = (DeliveryTypeTimeEnum::PREVIOUSLY_CONFIRMED === $itemTimeType)
? DeliveryTypeTimeEnum::CALCULATED
: $itemTimeType;
$artTimeTypes[] = $itemTimeType;
$item->setArticleTimeType($itemTimeType);
}
}
}
// меняем статус об ожидании подтверждения клиентом заказа
if ($timeConfirm) {
$this->setStatus($order, BuyOrderStatusEnum::PAID_NOT_CONFIRMED);
}
// если есть
if (
count($artTimeTypes) > 0
&& $order->getStatus()
&& $order->getStatus() != BuyOrderStatusEnum::PAID_NOT_CONFIRMED
) {
$order->setDeliveryTimeType(min($artTimeTypes));
}
// Только после setDeliveryTimeType
if (isset($delivery['calculateType'])) {
$order->setDeliveryCalculateType(!empty($delivery['calculateType']));
} elseif ($order->getDeliveryCalculateType() == null) {
// Назначаем авто расчет сроков, если тип расчета не был передан
$order->setDeliveryCalculateType(1);
}
// если по заказу не запрашивали счет и срок рассчитан автоматически, срок предложения ставим в 1 день
if (
$order->getPayDateExecution() != null
&& $order->getDeliveryTimeType() === DeliveryTypeTimeEnum::CALCULATED
&& $order->getBankTransferRequested() == null
&& !empty($data['status']) && $data['status'] == 3
) {
$d = new DateTime(date('Y-m-d', strtotime('now')) . ' 23:59:59');
$this->log(
'PAYDATEEXECUTION',
$order->getHash(),
[
'was' => $order->getPayDateExecution()->format('Y-m-d H:i:s'),
'now' => $d->format('Y-m-d H:i:s'),
]
);
$order->setPayDateExecution($d);
}
// если сроки все же не проставлены, а статус заказа не черновик ставим старые
if ($order->getStatus() && $order->getStatus() > BuyOrderStatusEnum::AWAITING_CALCULATION) {
if ($order->getDeliveryTimeMin() == null) {
$order->setDeliveryTimeMin($order->getDeliveryOldTimeMin());
}
if ($order->getDeliveryTimeMax() == null) {
$order->setDeliveryTimeMax($order->getDeliveryOldTimeMax());
}
}
}
// специальное письмо для образцов при возможности доставки раньше
if (OrderHelper::samplesDeliveryFaster($order, $oldDate)) {
$this->historyService->saveHistoryAndSendEmail(
$order,
'order_will_be_delivered_faster',
OrderHistoryService::SEND_EMAIL,
['%oldTimeDelivery%' => $oldTimeDelivery]
);
$order->setDeliveryTimeType(DeliveryTypeTimeEnum::CONFIRMED);
}
// отправка отложенного письма
if (
$order->getKeyEmail() != null
&& $order->getStatus()
&& BuyOrderStatusEnum::isRequiredNotice($order->getStatus())
) {
// отправляем письмо о сбросе, только если сроки не рассчитались
// и если есть артикулы в заказе
if ($order->getDeliveryTimeMin() == null && $order->getArticles()->count() > 0) {
$this->historyService->saveHistoryAndSendEmail(
$order,
$order->getKeyEmail(),
OrderHistoryService::SEND_EMAIL
);
$order->setKeyEmail(null);
}
}
}
if (!empty($data['deliveryOld'])) { //старый срок поставки по заказу
if (!empty($data['deliveryOld']['timeMin'])) {
if ($timeMin = DateHelper::standard($data['deliveryOld']['timeMin'])) {
$order->setDeliveryOldTimeMin($timeMin);
}
}
if (!empty($data['deliveryOld']['timeMax'])) {
if ($timeMax = DateHelper::standard($data['deliveryOld']['timeMax'])) {
$order->setDeliveryOldTimeMax($timeMax);
}
}
}
if (!empty($data['deliveryNew'])) { // новый срок поставки по заказу
if (!empty($data['deliveryNew']['timeMin'])) {
if ($timeMin = DateHelper::standard($data['deliveryNew']['timeMin'])) {
$order->setDeliveryTimeMin($timeMin);
}
}
if (!empty($data['deliveryNew']['timeMax'])) {
if ($timeMax = DateHelper::standard($data['deliveryNew']['timeMax'])) {
$order->setDeliveryTimeMax($timeMax);
}
}
}
}
/**
* Заполняет в заказе данные на основании ответа от "1С"
* Не должен содержать бизнес логику
*
* @param BuyOrder $order
* @param array $data
* @throws Exception
*/
private function setDataFromResponse(BuyOrder $order, array $data): array
{
// помечаем зеркало, через которое было заполнение данных
$order->setMirror($this->mirror);
// если еще не сохранялась старая версия данных заказа
$this->oldOrder ??= clone $order;
if (isset($data['dateCancel'])) {
$order->setCancelDate(DateHelper::standard($data['dateCancel']));
}
$this->setManagerToOrder($order, $data['manager'] ?? null);
if (isset($data['showMSG'])) {
$order->setShowMSG(!empty($data['showMSG']));
}
if (isset($data['reason'])) {
$order->setWriteOffReason((int) $data['reason']);
}
if (isset($data['recipientNumber'])) {
$order->setRecipientNumber(strip_tags($data['recipientNumber']));
}
if (isset($data['weightLimit'])) {
$weightLimit = (float) str_replace(',', '.', $data['weightLimit']);
$order->setWeightLimit($weightLimit);
}
if (isset($data['customsPayment'])) {
$customsPayment = (float) str_replace(',', '.', $data['customsPayment']);
$order->setCustomsPayment($customsPayment);
}
if (isset($data['number'])) {
$order->setNumber($data['number']);
}
if (!empty($data['numberDonor'])) {
$order->setNumberDonor($data['numberDonor']);
}
if (!empty($data['acceptPay'])) {
$order->setAcceptPay((bool) $data['acceptPay']);
}
if (!empty($data['reDelivery'])) {
$order->setDeliveryStatus(true);
}
if (!empty($data['reserveCreated'])) {
$order->setReserve(new DateTime());
}
if (isset($data['carrier']['name'])) {
$order->setCarrierName(empty($data['carrier']['name']) ? null : $data['carrier']['name']);
}
if (isset($data['carrier']['contactData'])) {
$order->setCarrierContactData(
empty($data['carrier']['contactData']) ? null : $data['carrier']['contactData']
);
}
if (isset($data['carrier']['tracking'])) {
$order->setTrackingNumber(empty($data['carrier']['tracking']) ? null : $data['carrier']['tracking']);
}
// дата отправки заказа
if (!empty($data['sendingDate'])) {
$order->setSendingDate(DateHelper::standard($data['sendingDate']));
}
// предполагаемая дата отгрузки
if (!empty($data['dateStock'])) {
$order->setShipDate(DateHelper::standard($data['dateStock']));
}
// предполагаемая дата отгрузки
if (!empty($data['deliveryDateFact'])) {
$order->setFactDate(DateHelper::standard($data['deliveryDateFact']));
}
if (isset($data['refNumber'])) {
$order->setRefNumber($data['refNumber']);
}
if (!empty($data['sendReview'])) {
$order->setSendReview($data['sendReview']);
}
if (!empty($data['warehouseAddress'])) {
$warehouseCode = !empty($data['code']) ? $data['code'] : null;
$warehouseAddress = $order->getWarehouseData();
$warehouseAddress['code'] = $warehouseCode ?: '';
$warehouseAddress['schedule'] = !empty($data['schedule'])
? ($warehouseCode ? $this->translationService->trans('schedule_' . $warehouseCode) : $data['schedule'])
: '';
$warehouseAddress['warehouseAddress'] = $warehouseCode
? $this->translationService->trans('warehouse_' . $warehouseCode)
: $data['warehouseAddress'];
$warehouseAddress['warehouseContact'] = !empty($data['warehouseContact']) ? $data['warehouseContact'] : '';
$warehouseAddress['warehouseSchema'] = !empty($data['warehouseSchema']) ? $data['warehouseSchema'] : '';
if (isset($data['warehouseLanguage'])) {
$warehouseAddress['warehouseLanguage'] = !empty($data['warehouseLanguage']) ? $data['warehouseLanguage'] : '';
}
if (isset($data['warehouseEmail'])) {
$warehouseAddress['warehouseEmail'] = !empty($data['warehouseEmail']) ? $data['warehouseEmail'] : '';
}
$order->setWarehouseData($warehouseAddress);
}
$this->setDeliveryOrder($data, $order);
if (isset($data['proposedDate'])) {
$order->setProposedDate(new DateTime($data['proposedDate']));
// устанавливаем тип периода с конкретной датой поставки
$order->setDeliveryTimeType(4);
// ставим статус в пути
$this->setStatus($order, BuyOrderStatusEnum::TRANSIT);
$data['status'] = BuyOrderStatusEnum::TRANSIT;
$tableParams = [];
if (!empty($data['carrierPhone'])) {
$tableParams['carrierPhone'] = $data['carrierPhone'];
}
if (!empty($data['carrierSite'])) {
$tableParams['carrierSite'] = $data['carrierSite'];
}
if (!empty($data['proposedDateEx'])) {
$tableParams['proposedDateEx'] = $data['proposedDateEx'];
}
// если отправлен неполный заказ
if (!empty($data['table2'])) {
$alias = $order->getDeliveryCountry() && in_array($order->getDeliveryCountry()->getId(), [124, 840])
? (OrderHelper::isCarrierData($order)
? (!empty($data['proposedDateEx'])
? 'particle_delivery_without_transitor_data_2b'
: 'calculated_date_for_article_delivery_usa_tracking'
)
: (!empty($data['proposedDateEx'])
? 'particle_delivery_without_transitor_data_2a'
: 'calculated_date_for_article_delivery_usa'
)
)
: (OrderHelper::isCarrierData($order)
? (!empty($data['proposedDateEx'])
? 'particle_delivery_without_transitor_data_2b'
: 'calculated_date_article_delivery_tracking'
)
: (!empty($data['proposedDateEx'])
? 'particle_delivery_without_transitor_data_2a'
: 'calculated_date_for_article_delivery'
)
);
// если дополнительная поставка заказа
if (isset($data['reDelivery']) && $data['reDelivery']) {
$alias = $order->getDeliveryCountry() && in_array($order->getDeliveryCountry()->getId(), [124, 840])
? (OrderHelper::isCarrierData($order)
? 'additional_delivery_container_tk_data'
: 'additional_delivery_container_without_tk_data'
)
: (OrderHelper::isCarrierData($order)
? 'without_using_container_segment_tk_data'
: 'without_using_container_segment_without_tk_data'
);
}
$tableParams['table2'] = $data['table2'];
} else {
$alias = $order->getDeliveryCountry() && in_array($order->getDeliveryCountry()->getId(), [124, 840])
? (OrderHelper::isCarrierData($order)
? (!empty($data['proposedDateEx'])
? 'fully_delivery_without_transitor_data_1b'
: 'order_will_be_delivered_soon_tracking'
)
: (!empty($data['proposedDateEx'])
? 'fully_delivery_without_transitor_data_1a'
: 'order_will_be_delivered_soon'
)
)
: (OrderHelper::isCarrierData($order)
? (!empty($data['proposedDateEx'])
? 'fully_delivery_without_transitor_data_1b'
: 'transit_tracking'
)
: (!empty($data['proposedDateEx'])
? 'fully_delivery_without_transitor_data_1a'
: 'transit'
)
);
}
$this->historyService->saveHistoryAndSendEmail(
$order,
$alias,
OrderHistoryService::SEND_EMAIL,
$tableParams
);
}
$params = [];
if (!empty($data['fileBase64'])) {
$params['fileBase64'] = $data['fileBase64'];
}
if (!empty($data['table2'])) {
$params['table2'] = $data['table2'];
}
if (!empty($data['LetterVariant'])) {
$this->historyService->sendInvoice($order, $data);
}
if (!empty($data['compensation'])) {
$order->setCompensation((float) str_replace(',', '.', $data['compensation']));
// Рома сказал не фиксировать возврат при назначении компенсации
}
if (!empty($data['sumComp'])) {
$params['sumComp'] = $data['sumComp'];
}
if (!empty($data['sumCurrency'])) {
$params['sumCurrency'] = $data['sumCurrency'];
}
// обязательно после функции setDeliveryOrder - установки сроков поставки
if (!empty($data['status'])) {
$newStatus = (int) $data['status'];
// если оплата пришла от "1С" и дата действия платежа с просрочкой,
// то надо слать метку в функцию смены статуса для отправки соответствующего письма
$params['fromOneC'] = !empty($data['checkHash']);
// при статусе 19 нужно так же передать фабрику, которая стала причиной отмены заказа
if (!empty($data['factory'])) {
$params['%factory%'] = $data['factory'];
}
if ($newStatus === BuyOrderStatusEnum::PAID && !OrderHelper::checkPayDateExecution($order)) {
$params['oldPayment'] = true;
}
if (isset($data['table3'])) {
// если есть, то передаем данные физических параметров скомплектованного заказа
$params['table3'] = $data['table3'];
}
if (isset($data['sum'])) {
$params['sum'] = $data['sum'];
}
if (isset($data['underpayment'])) {
$params['underpayment'] = $data['underpayment'];
}
if (isset($data['sumOrder'])) {
$params['sumOrder'] = $data['sumOrder'];
}
if (!empty($data['reason'])) {
$params['reason'] = $data['reason'];
}
if (!empty($data['sumCurrency'])) {
$params['sumCurrency'] = $data['sumCurrency'];
}
if (!empty($data['proposedDateEx'])) {
$params['proposedDateEx'] = $data['proposedDateEx'];
}
if (
$newStatus !== BuyOrderStatusEnum::REQUEST_DELIVERY
|| $order->getStatus() != BuyOrderStatusEnum::AWAITING_CALCULATION
) {
$this->setStatus($order, $newStatus, $params);
}
}
return $params;
}
/**
* Возвращает список адресов складов
* @throws Exception
*/
public function getPickupWarehouses(?bool $onlyId = false): array
{
if ($onlyId) {
return $this->warehouseService->getIdsPickupWarehouses();
}
return $this->warehouseService->getPickupWarehouses();
}
/**
* Возвращает массив заказов клиента которые доступны для изменения
*
* @param string $token
* @return array
* @throws Exception
*/
public function orderShortList(string $token): array
{
$list = [];
/** @var BuyOrder $order */
foreach ($this->buyOrderRepository->getBuyOrders(['token' => $token, 'status' => [1, 2, 3]]) as $order) {
$list[] = $this->getProjectData($order);
}
return $list;
}
/**
* Проверяет актуальность предложения
*
* @param BuyOrder $order
* @param ?bool $noUpdate если true, то сброса и обновления не будет, вернёт только нужно ли обновление или нет
* @return bool
* @throws Exception
*/
public function checkingOffer(BuyOrder $order, ?bool $noUpdate = false): bool
{
if ($order->getStatus() >= BuyOrderStatusEnum::PARTIALLY_PAID) {
return false;
}
$change = false;
// todo убрать после теста
$this->addressService->mergeAddresses($order);
if ($order->getPaymentEuro() && $order->getCurrency() != 'EUR') {
$session = App::getRequest()->getSession();
if (!$session->get($order->getHash() . '-curRate')) {
$curRate = $this->em->getRepository('WebBundle:CurrencyRate')
->findOneBy(['currency' => $order->getCurrency()]);
$curRate = (float) ($curRate ? $curRate->getVal() : 1);
$session->set($order->getHash() . '-curRate', $curRate);
$order->setCurRate($curRate);
$this->buyOrderRepository->save($order);
}
}
// проверка изменений локализации
$this->localization($order, []);
$this->buyOrderRepository->save($order);
// берем только заказы по которым не было оплаты и заказ в обработке или рассчитанные
if (
in_array($order->getStatus(), [BuyOrderStatusEnum::AWAITING_PAYMENT, BuyOrderStatusEnum::AWAITING_CALCULATION])
|| $order->getStatus() == BuyOrderStatusEnum::REQUEST_DELIVERY
&& OrderHelper::readyCreate($order)
) {
if (
// обнуляем даты, если заказ не оплачен в установленный срок
!$order->getNoChange()
&& (
$order->getStatus() == BuyOrderStatusEnum::AWAITING_PAYMENT
&& !OrderHelper::checkPayDateExecution($order)
)
) {
// отключили чтобы не "спамить" клиента по распоряжению Ромы Загорудько
// заказ был посчитан вручную
// if ($order->getDeliveryCalculateType() > 1) {
// $this->historyService->saveHistory($order, 'reset_not_pay', 0);
// }
// Сброшены сроки поставки в следствии истечения времени на оплату
$order = $this->reset($order, ReasonForResetOrderEnum::EXPIRATION_OF_THE_PAYMENT_TIME(), 'Reset time delivery, reason - expiration of the payment time');
$change = true;
} elseif (
// если изменились данные влияющие на доставку
!$order->getNoChange()
&& $order->getMarkerReset() != OrderHelper::markerReset($order)
) {
// В заказе были изменения, влияющие на расчет доставки
$order = $this->reset($order, ReasonForResetOrderEnum::CHANGE_ORDER(), 'Reset time delivery, reason - change of data');
if ($order->getStatus() == BuyOrderStatusEnum::AWAITING_CALCULATION) { // был запрошен расчёт доставка
$this->historyService->saveHistoryAndSendEmail(
$order,
'reset_change',
OrderHistoryService::SEND_EMAIL
);
}
$change = true;
} elseif ($order->getStatus() == BuyOrderStatusEnum::REQUEST_DELIVERY && !OrderHelper::checkPayDateExecution($order)) {
$order = $this->reset($order, ReasonForResetOrderEnum::CHECK_PAY_DATE_EXECUTION(), 'Reset checkPayDateExecution');
$change = true;
}
}
if ($noUpdate || $this->asConsole) {
return $change;
}
// были изменения в заказе, надо прогнать через change
if (
$change
|| !$order->getNoChange()
&& (
$order->getMarkerChange() != OrderHelper::md5MarkerChange($order)
|| !$order->getSendCreate1C()
)
) {
$this->changeByOrder(
$order,
json_encode(OrderHelper::itemsArr($order, ['id' => $order->getHash()]))
);
}
return $change;
}
/**
* @param BuyOrder $order
* @param array $data
* @throws Exception
*/
public function localization(BuyOrder $order, array $data)
{
$this->cloneOrderIfNeeded($order);
if (!$order->getUser() && $order->getAddressRecipient() && $order->getAddressRecipient()->getUser()) {
$order->setUser($order->getAddressRecipient()->getUser());
}
// эти данные меняет только хозяин заказа
if ($this->token !== $order->getToken()) {
return;
}
if (!empty($data['locale'])) {
$langArr = explode('|', App::getParameter('langs_available'));
if (count($langArr) > 0) {
if (!in_array($data['locale'], $langArr)) {
$data['locale'] = $langArr[0];
}
if ($order->getLocale() != $data['locale']) {
$curDate = new DateTime('NOW');
$this->log(
'CHANGE LOCALE',
$order->getHash(),
[
'oldLocale' => $order->getLocale(),
'newLocale' => $data['locale'],
'date' => $curDate->format('Y-m-d H:i:s'),
]
);
$order->setLocale($data['locale']);
}
}
}
// получаем код страны
$codeISO = StrHelper::toLower(
$order->getAddressRecipient() && $order->getAddressRecipient()->getCountry()
? $order->getAddressRecipient()->getCountry()->getCode()
: App::getCurCountry()
);
$currencyArr = LocaleHelper::getCurrencyList($codeISO);
$measureArr = LocaleHelper::getMeasureList($codeISO);
$data['currency'] = empty($data['currency']) ? $order->getCurrency() : $data['currency'];
$data['measure'] = empty($data['measure']) ? $order->getMeasure() : $data['measure'];
// для США и канады надо сис счисления брать из url в новом заказе
if (
in_array($codeISO, ['us', 'ca'])
&& $order->getMeasure() != CookieHelper::get(CookieKeysConstant::MEASURE_FT)
) {
$data['measure'] = CookieHelper::get(CookieKeysConstant::MEASURE_FT);
}
// для России, в случае, если заказ образцов, то валюту ставим Euro
if (OrderHelper::onlySamples($order) && $codeISO == 'ru') {
$data['currency'] = 'EUR';
}
if (count($currencyArr) > 0) {
// для России в случае, если заказ образцов, то валюту ставим Euro
if (OrderHelper::onlySamples($order) && $codeISO == 'ru') {
$currencyArr[] = 'EUR';
}
if (!in_array($data['currency'], $currencyArr)) {
$data['currency'] = $currencyArr[0];
}
if ($order->getCurrency() != $data['currency']) {
$order->setCurrency($data['currency']);
}
}
if (count($measureArr) > 0) {
if (!in_array($data['measure'], $measureArr)) {
$data['measure'] = $measureArr[0];
}
if ($order->getMeasure() != $data['measure']) {
$order->setMeasure($data['measure']);
}
}
// обрабатываем данные по пользователю
$this->setUserData($order);
}
/**
* Возвращает название для заказа
*
* @param string $token Токен пользователя
* @param string $name Текущее название
* @param ?int $id Идентификатор заказа
* @return string
* @throws Exception
*/
protected function getNameOrder(string $token, string $name, ?int $id = null): string
{
$name = strip_tags(urldecode($name));
$orders = $this->buyOrderRepository->getNames($token, $id);
$locale = App::getCurLocale();
/** @var $order BuyOrder */
$names = [];
foreach ($orders as $order) {
$names[] = str_replace(
'%orderName%',
$this->translationService->trans('buyOrder.name' . ($order['status'] < 4 ? 3 : ''), $locale),
$order['name']
);
}
$newName = $name;
$num = 1;
while (in_array($newName, $names)) {
$num++;
$newName = $name . ' (' . $num . ')';
}
return $newName;
}
/**
* @param string $url
* @param array|null $data
* @return Response
* @throws Exception
*/
public function getPdfInPage(string $url, ?array $data = []): Response
{
$snappy = App::getContainer()->get('knp_snappy.pdf');
$session = App::getRequest()->getSession();
$session->save();
$snappy->setTimeout(120);
$orderNumber = $data['orderNumber'] ?? '';
return new Response(
$snappy->getOutput(
$url,
[
'orientation' => 'Landscape',
'page-size' => 'A4',
'print-media-type' => true,
'cookie' => $_COOKIE,
]
),
200,
[
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="tile.expert-' . $orderNumber . '.pdf"',
]
);
}
/**
*
* @param $order
* @return Response
* @throws LoaderError
* @throws RuntimeError
* @throws SyntaxError
* @throws Exception
*/
public function getPdfOrder($order): Response
{
/** @var BuyOrder $order */
$snappy = App::getContainer()->get('knp_snappy.pdf');
$itemsData = OrderHelper::dataForLetter($order);
$html = App::getTwig()->render('@Web/BuyOrder/order-pdf.html.twig', [
'order' => $order,
'articles' => $itemsData,
'curLocale' => App::getCurLocale(),
]);
$html = str_replace('"/', '"' . App::getParameter('full_domain') . '/', $html);
return new PdfResponse(
$snappy->getOutputFromHtml($html),
'tile.expert-' . $order->getNumber() . '.pdf'
);
}
/**
* @param BuyOrder $order
* @return Response
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
*/
public function convertToExcel($order): Response
{
$items = OrderHelper::dataForLetter($order);
$orderNumber = $order->getNumber() ?: $order->getHash();
ob_start();
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getColumnDimension('A')->setWidth(100);
$sheet->getColumnDimension('B')->setWidth(30);
$sheet->getColumnDimension('C')->setWidth(30);
$sheet->getColumnDimension('D')->setWidth(30);
$sheet->getColumnDimension('E')->setWidth(30);
$sheet->getColumnDimension('F')->setWidth(30);
$sheet->setCellValue('A1', $this->translationService->trans('buyOrder.th.article'));
$sheet->setCellValue(
'B1',
$this->translationService->trans('buyOrder.th.factory') . " / " . $this->translationService->trans('buyOrder.th.collection')
);
$sheet->setCellValue('C1', $this->translationService->trans('buyOrder.th.price'));
$sheet->setCellValue('D1', $this->translationService->trans('buyOrder.th.quality'));
$sheet->setCellValue('E1', $this->translationService->trans('buyOrder.th.amount'));
$sheet->setCellValue('F1', $this->translationService->trans('buyOrder.info.delivery.estimated_date'));
$counter = 2;
foreach ($items as $item) {
$itemSize = '';
if ($item['type'] == 3) {
$sampleType = OrderItemHelper::humanSampleType($item['sampleType']);
$itemSize .= $sampleType['name'];
$itemSize .= ' ' . OrderItemHelper::sampleFormat(
$item,
$item['orderParam'],
$item['orderMeasure'] == 'ft'
);
} else {
if ($item['orderMeasure'] == 'ft') {
$itemSize .= '~' . ConversionHelper::convertInch((float) $item['sizeX'], ConversionHelper::CM) .
'″х' . ConversionHelper::convertInch((float) $item['sizeY'], ConversionHelper::CM) . '″';
} else {
$itemSize .= $item['sizeX'] . 'x' . $item['sizeY'] . ' ' . $this->translationService->trans('left_menu_cm');
}
}
$itemSize = html_entity_decode(strip_tags($itemSize));
$material = $item['material'] ? $this->translationService->trans($item['material']->getAlias()) : '';
$sheet->setCellValue(
'A' . $counter,
$material . ' ' . $item['name'] . ' ' .
$this->translationService->trans('collection_by') . ' ' . $item['collection']->getFactory()->getName() . ' ' . $itemSize
);
$factoryName = $item['collection']->getFactory() ? $item['collection']->getFactory()->getActualName() : "";
$sheet->setCellValue(
'B' . $counter,
$item['collection']->getActualName() . " / " . $factoryName
);
$sheet->setCellValue(
'C' . $counter,
$item['price'] . ' ' . $item['currency'] . '/' . $this->translationService->trans($item['measure'])
);
$measure = (isset($item['measure']) && !empty($item['measure'])) ? $this->translationService->trans($item['measure']) : '';
$boxes = ($item['type'] == 1) ? $item['boxes'] : 1;
$sheet->setCellValue(
'D' . $counter,
(floor($item['amount'] * 100) / 100) . ' ' . $measure . ' / ' . $boxes
);
$sheet->setCellValue('E' . $counter, round($item['price'] * $item['amount'], 2) . ' ' . $item['currency']);
$sheet->setCellValue('F' . $counter, strip_tags(OrderItemHelper::getItemTime($item['article'])));
$counter++;
}
$summary = $this->getOrderSummary($order);
foreach ($summary as $item) {
$sheet->setCellValue(
'A' . (++$counter),
$item["title"]
);
$value = html_entity_decode(strip_tags($item["value"]));
$sheet->setCellValue(
'B' . ($counter),
$value
);
}
$writer = new Xlsx($spreadsheet);
$writer->save('php://output');
return new Response(
ob_get_clean(),
200,
[
'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'Content-Disposition' => 'attachment; filename="tile.expert-' . $orderNumber . '.xlsx"',
]
);
}
/**
* Return order summary values
*
* @param BuyOrder $order
* @return array
* @throws Exception
*/
public function getOrderSummary(BuyOrder $order): array
{
$sum = OrderHelper::sum($order);
$vatCondition = OrderHelper::asVat($order) == false || OrderHelper::homeCountryVat($order);
$payType = 'buyOrder.info.payment_cost.payPal';
switch ($order->getPayType()) {
case PaymentTypeEnum::BANK_TRANSFER:
$payType = 'buyOrder.info.payment_cost.Bank';
break;
case PaymentTypeEnum::VISA_MASTERCARD:
$payType = 'buyOrder.info.payment_cost.Card';
break;
}
$summary = [
"goods" => [
"title" => 'buyOrder.info.goods',
"value" => $vatCondition ? $sum['all'] : $sum['sum'],
"currency" => true,
],
"delivery_cost" => [
"title" => 'buyOrder.info.delivery_cost',
"value" => $order->getDelivery() !== null ? $order->getDelivery() : $this->translationService->trans(
'order.status.request_delivery'
),
"currency" => LocaleHelper::getCurrency($order->getCurrency()),
],
"vat" => [
"title" => 'buyOrder.info.VAT.row-name',
"value" => OrderHelper::vatPercent($order),
"percents" => true,
],
"payment_percent" => [
"title" => $payType,
"value" => OrderHelper::paymentPercent($order),
"currency" => true,
],
"total" => [
"title" => 'buyOrder.info.total',
"value" => OrderHelper::totalSum($order),
"currency" => true,
],
"delivery_to" => [
"title" => 'order.delivery_to',
"value" => preg_replace(
"/(^,)|(,$)/",
"",
join(',', [
$order->getDeliveryIndex(),
$order->getDeliveryCountry(),
$order->getDeliveryCity(),
])
),
],
];
foreach ($summary as $key => &$item) {
$item["title"] = preg_replace('/%d% /', '', $this->translationService->trans($item["title"]));
if (!empty($item["currency"])) {
$item["value"] .= " " . LocaleHelper::getCurrency($order->getCurrency());
} else {
if (!empty($item["percents"])) {
$item["value"] .= " %";
}
}
}
return $summary;
}
/**
* @param string $hash
* @return array
* @throws NonUniqueResultException
* @throws Exception
*/
public function delAllData(string $hash): array
{
$res = [
'user' => false,
'orders' => 0,
];
$order = $this->getOrderObj($hash, true);
if (!$this->isAccessAdminOrder($order)) {
throw new ForbiddenOverwriteException('Access forbidden!');
}
$user = $order->getUser();
if ($user) {
$res['user'] = true;
// Пишем лог по пользователю
$this->log(
'DELALLDATAUSER',
$user->getId(),
$this->logMasterUser([
'id' => $user->getId(),
'unid' => $user->getUnid(),
'email' => $user->getEmail(),
'token' => $user->getToken(),
])
);
$this->em->remove($user);
}
foreach ($this->buyOrderRepository->findByToken($order->getToken() ?? '') as $o) {
//Пишем лог по заказу
$this->log(
'DELALLDATAORDER',
$o->getHash(),
OrderHelper::arrayData($o, true)
);
$this->em->remove($o);
$res['orders']++;
}
$res['msg'] = 'Deleted!';
$this->em->flush();
return $res;
}
/**
* @param string $hash
* @return RedirectResponse
* @throws NonUniqueResultException
* @throws Exception
*/
public function recoveryCancelOrder(string $hash): RedirectResponse
{
$order = $this->getOrderObj($hash, true);
if (!$this->isAccessAdminOrder($order)) {
throw new ForbiddenOverwriteException('Access forbidden!');
}
$order->setStatus(BuyOrderStatusEnum::REQUEST_DELIVERY);
$this->buyOrderRepository->save($order);
$this->log(
'RECOVERYORDER',
$order->getHash(),
$this->logMasterUser(OrderHelper::arrayData($order))
);
return new RedirectResponse(App::generateUrl('app_buy_order', ['orderId' => $hash]));
}
/**
* Проверка доступа
* @param BuyOrder $order
* @return bool
* @throws Exception
*/
public function isAccessAdminOrder(BuyOrder $order): bool
{
$masterUser = App::getCurUser();
if (
$masterUser
&& ($this->authorizationChecker->isGranted('ROLE_CONS')
&& (
StrHelper::toLower($masterUser->getEmail()) == StrHelper::toLower($order->getManagerEmail())
|| StrHelper::toLower($masterUser->getUsername()) == StrHelper::toLower($order->getManagerLogin())
)
|| $this->authorizationChecker->isGranted('ROLE_SUPER_ADMIN')
)
&& ($order->getUser() == null
|| strpos(StrHelper::toLower($order->getUser()->getEmail()), 'tile.expert') === false
&& strpos(StrHelper::toLower($order->getUser()->getEmail()), 'treto.ru') === false
)
) {
return true;
}
return false;
}
/**
* Проверка, является ли выполняемое изменение статуса запрещенным
* @param int $status
* @param BuyOrder $order
* @return bool
* @see OrderServiceSetStatusTest::testIsForbiddenStatusChange()
*
*/
private function isForbiddenStatusChange(int $status, BuyOrder $order): bool
{
/* Смена статуса заказа в черновик */
if (
$status < BuyOrderStatusEnum::PARTIALLY_PAID
&& $order->getStatus() > BuyOrderStatusEnum::AWAITING_PAYMENT
) {
return true;
}
/* Проверка, является ли новый статус допустимым при нынешнем статусе "Частично оплачено" */
/* Входит ли новый статус в список запрещённых для смены из частично оплаченного */
if (
$order->getStatus() === BuyOrderStatusEnum::PARTIALLY_PAID
&& in_array(
$status,
[
BuyOrderStatusEnum::REQUEST_DELIVERY,
BuyOrderStatusEnum::AWAITING_CALCULATION,
BuyOrderStatusEnum::AWAITING_PAYMENT,
BuyOrderStatusEnum::DELETED,
]
)
) {
return true;
}
return false;
}
/**
* Добавляет инфо о пользователе инициировавшем обработку
* @param array $data
* @return array
* @throws Exception
*/
private function logMasterUser(array $data): array
{
$user = App::getCurUser();
return array_merge($data, [
'masterUser' => [
'token' => $this->token,
'unid' => $user ? $user->getUnid() : null,
'username' => $user ? $user->getUsername() : null,
],
]);
}
/**
* Добавление/изменение артикула
*
* @param int $itemId
* @param array $ordersAmount
* @param null|float $itemCount
* @param int|null $type - тип позиции
* @return array|bool
* @throws NonUniqueResultException
* @throws ORMException
* @throws Exception
*/
public function changeElement(
int $itemId,
array $ordersAmount,
?float $itemCount = null,
?int $type = BuyOrderArticle::TYPE_NORMAL
) {
// заказ можно менять только до момента оплаты
$params['status'] = [1, 2, 3];
$params['token'] = $this->token;
if (count($ordersAmount) > 0) {
$params['hashes'] = array_keys($ordersAmount);
}
$item = $this->articleRepository->getArticle($itemId);
if (!$item) {
return false;
}
$addedToCart = $item->getAddedToCart();
$count = 0;
$orders = [];
$clientExistsOrders = $this->buyOrderRepository->getBuyOrders($params);
foreach ($clientExistsOrders as $order) {
/** @var BuyOrder $order */
if (
OrderHelper::canIAddThatTypeToOrder($order, $type ?? BuyOrderArticle::TYPE_NORMAL)
&& (in_array($order->getHash(), $params['hashes'] ?? [])
|| $order->getStatus() == BuyOrderStatusEnum::REQUEST_DELIVERY
|| (
$order->getStatus() == BuyOrderStatusEnum::AWAITING_PAYMENT
&& $order->getDeliveryCalculateType() !== 0
)
)
) {
$orders[] = $order;
continue;
}
if (isset($ordersAmount[$order->getHash()]) && $ordersAmount[$order->getHash()] === 0) {
$orders[] = $order;
}
}
// если у клиента еще нет заказа, или нет заказа, в который можем добавить, создаем новый
if (empty($orders)) {
$orders[] = $this->create($this->token);
}
// для установки типа оплаты по умолчанию
if (
$type === BuyOrderArticle::TYPE_SAMPLE
&& count($orders) === 1
&& current($orders)->getArticles()->count() == 0
) {
$order = current($orders);
$order->setPayType(OrderHelper::country($order) == 643 ? PaymentTypeEnum::VISA_MASTERCARD : PaymentTypeEnum::BANK_TRANSFER);
}
$projects = [];
$ordersHasSample = [];
foreach ($orders as $order) {
$orderHash = $order->getHash();
// ищем артикул в заказе
$orderItem = null;
/** @var BuyOrderArticle|null $orderItem */
if ($order->getArticles()) {
/** @var BuyOrderArticle $oneArticleInCurrentOrder */
foreach ($order->getArticles() as $oneArticleInCurrentOrder) {
if (
$oneArticleInCurrentOrder->getArticle()->getId() === $item->getId()
&& $oneArticleInCurrentOrder->getType() === ($type ?? BuyOrderArticle::TYPE_NORMAL)
) {
$orderItem = $oneArticleInCurrentOrder;
break;
}
}
$count += $order->getArticles()->count();
}
// если не установлено количество для выбранных заказов, ставим 0.1
if (
!array_key_exists($orderHash, $ordersAmount)
|| (
empty($ordersAmount[$orderHash])
&& !empty($itemCount)
)
) {
$ordersAmount[$orderHash] = !empty($itemCount) ? $itemCount : 0.1;
}
if (!empty($ordersAmount[$orderHash])) {
$amount = round(
LocaleHelper::getAmount(
$item,
(float) $ordersAmount[$orderHash],
OrderHelper::params(
$order,
$item->getMeasure()
? ['measureId' => $item->getMeasure()->getId()]
: []
),
$type
),
5
);
// еще не добавляли артикул в заказ
if (null === $orderItem) {
$key = 'ADDITEM';
$item->setAddedToCart($addedToCart + 1);
$orderItem = $this->itemService->createOrderItem($item, $type, $order);
$this->itemService->setItemData($order, $orderItem, $amount, $type);
if ($orderItem->getType() == BuyOrderArticle::TYPE_SAMPLE) {
// Проверка глобальных остатков образца
$sampleCode = $orderItem->chooseCode();
$country = OrderHelper::country($order);
try {
$sampleData = $this->sampleService->getSampleData(
$sampleCode,
$country,
false
);
$availableAmount = (float) ($sampleData['amountRem'] ?? 0);
$reservedCount = $this->getReservedSampleCount($sampleCode, $order->getHash());
$requestedAmount = (float) $orderItem->getAmount();
$maxAvailable = max(0, $availableAmount - $reservedCount);
if ($reservedCount + $requestedAmount > $availableAmount) {
if ($maxAvailable <= 0) {
$order->removeArticle($orderItem);
$this->em->remove($orderItem);
$this->em->flush();
return [
'msg' => [
'header' => $this->translationService->trans(
'sample_not_enough_stock.header',
App::getCurLocale(),
[
'%article%' => $sampleCode,
]
),
'body' => $this->translationService->trans(
'sample_not_enough_stock.body',
App::getCurLocale(),
[
'%article%' => $sampleCode,
'%available%' => 0,
]
),
],
'count' => $count,
'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount($this->token),
'projects' => $projects,
];
}
$orderItem->setAmount($maxAvailable);
$this->em->flush();
}
} catch (\Exception $e) {
// Если не удалось получить данные образца из 1C, продолжаем без проверки
$this->logger->error('Failed to check sample availability: ' . $e->getMessage());
}
foreach ($order->getArticles() as $oItem) {
if ($orderItem->getId() == $oItem->getId()) {
continue;
}
if (
$oItem->getType() == BuyOrderArticle::TYPE_SAMPLE
&& $res = $this->checkingDoubleSample(
$orderItem,
(int) $oItem->getId(),
(string) $oItem->chooseCode()
)
) {
$ordersHasSample[] = $res;
break;
}
}
}
$count++;
} elseif (round($amount, 5) != round($orderItem->getAmount(), 5)) {
$key = 'CHANGEITEM';
$this->itemService->setItemData($order, $orderItem, $amount, $orderItem->getType());
if ($orderItem->getType() == BuyOrderArticle::TYPE_SAMPLE) {
$sampleCode = $orderItem->chooseCode();
$country = OrderHelper::country($order);
try {
$sampleData = $this->sampleService->getSampleData(
$sampleCode,
$country,
false
);
$availableAmount = (float) ($sampleData['amountRem'] ?? 0);
$reservedCount = $this->getReservedSampleCount($sampleCode, $order->getHash());
$requestedAmount = (float) $orderItem->getAmount();
$maxAvailable = max(0, $availableAmount - $reservedCount);
if ($reservedCount + $requestedAmount > $availableAmount) {
if ($maxAvailable <= 0) {
$order->removeArticle($orderItem);
$this->em->remove($orderItem);
$this->em->flush();
return [
'msg' => [
'header' => $this->translationService->trans(
'sample_not_enough_stock.header',
App::getCurLocale(),
[
'%article%' => $sampleCode,
]
),
'body' => $this->translationService->trans(
'sample_not_enough_stock.body',
App::getCurLocale(),
[
'%article%' => $sampleCode,
'%available%' => 0,
]
),
],
'count' => $count,
'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount($this->token),
'projects' => $projects,
];
}
$orderItem->setAmount($maxAvailable);
$this->em->flush();
}
} catch (\Exception $e) {
$this->logger->error('Failed to check sample availability on update: ' . $e->getMessage());
}
}
$count++;
} else {
// ТК ничего не изменилось - не будем логировать и сохранять
continue;
}
$this->log(
$key,
$order->getHash(),
[
'name' => $orderItem->getArticle()->getName(),
'amount' => $orderItem->getAmount(),
'type' => $orderItem->getType(),
'measure' => $orderItem->getMeasure(),
'currency' => $orderItem->getCurrency(),
]
);
$this->setStatus($order, BuyOrderStatusEnum::REQUEST_DELIVERY);
try {
// что-то изменили - зафиксируем
$this->em->persist($orderItem);
$this->em->persist($item);
$this->em->persist($order);
$this->em->flush();
} catch (Exception $exception) {
$this->log(
'CRASH_UPDATE_ORDER',
$order->getHash(),
[
'name' => $orderItem->getArticle()->getName(),
'amount' => $orderItem->getAmount(),
'type' => $orderItem->getType(),
'measure' => $orderItem->getMeasure(),
'currency' => $orderItem->getCurrency(),
]
);
throw $exception;
}
// просто сбрасываем в 1С - что заказ изменён
$this->reset($order, ReasonForResetOrderEnum::CHANGE_ORDER(), 'CHANGE ORDER');
}
$projects[] = $this->getProjectData($order);
;
}
$ordersHasSampleCount = count($ordersHasSample);
if ($ordersHasSampleCount == 1) {
return [
'msg' => [
'header' => $this->translationService->trans(
'sample_already_added.header',
App::getCurLocale(),
[
'%name%' => $item->getName(),
]
),
'body' => $this->translationService->trans(
'sample_already_added.body',
App::getCurLocale(),
[
'%nameOrder%' => $ordersHasSample[0],
]
),
],
'count' => $count,
'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount($this->token),
'projects' => $projects,
];
}
if ($ordersHasSampleCount > 1) {
return [
'msg' => [
'header' => $this->translationService->trans(
'sample_already_added.header',
App::getCurLocale(),
[
'%name%' => $item->getName(),
]
),
'body' => $this->translationService->trans(
'article.in_orders',
App::getCurLocale(),
[
'%d%' => join(', ', $ordersHasSample),
]
),
],
'count' => $count,
'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount($this->token),
'projects' => $projects,
];
}
return [
'count' => $count,
'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount($this->token),
'projects' => $projects,
];
}
/**
* @param string $hash
* @param bool $new
* @return bool|RedirectResponse
*/
public function checkHashRedirect(string $hash, ?bool $new = true)
{
if (mb_strlen($hash) < 30) {
$hash = $this->buyOrderRepository->getHashByNumber($hash);
if ($hash) {
if ($new) {
return new RedirectResponse($this->generateUrl('app_order', ['hash' => $hash]), 301);
}
return new RedirectResponse($this->generateUrl('app_buy_order', ['orderId' => $hash]), 301);
}
}
return false;
}
/**
* @param int $itemId
* @param int $type
* @param ?float $itemCount
* @return array
* @throws Exception
*/
public function getOrderAddList(int $itemId, int $type = BuyOrderArticle::TYPE_NORMAL, ?float $itemCount = 0.1): array
{
$response = [];
$orderList = [];
$orders = $this->buyOrderRepository->getBuyOrders([
'token' => $this->token,
'status' => [1, 2, 3],
'type' => $type,
]);
foreach ($orders as $order) {
if (OrderHelper::canIAddThatTypeToOrder($order, $type)) {
$orderList[] = $order;
}
}
$countOrder = count($orderList);
$isStatusAdd = false;
// если нет проекта в который можно добавить артикул, то создаём проект
if ($countOrder > 0) {
foreach ($orderList as $order) {
if (
$order->getStatus() == BuyOrderStatusEnum::REQUEST_DELIVERY
|| $order->getStatus() == BuyOrderStatusEnum::AWAITING_PAYMENT
&& $order->getDeliveryCalculateType() !== 0
) {
$isStatusAdd = true;
break;
} elseif ($order->getStatus() > BuyOrderStatusEnum::AWAITING_PAYMENT) {
--$countOrder;
}
}
}
//Если у клиента нет заказа, в который можно добавить артикул, создаём заказ
if ($countOrder === 0) {
$orderList[] = $this->create($this->token);
$isStatusAdd = true;
$countOrder = 1;
}
// Если у клиента только один доступный для изменения заказ, то сразу переадресовываем клиента на него
if ($isStatusAdd && $countOrder === 1) {
$response['redirect'] = $this->generateUrl(
'app_order_change_item',
[
'_locale' => App::getCurLocale(true),
'itemId' => $itemId,
'itemCount' => $itemCount ?? 1,
'type' => $type,
],
0
);
} else {
$active = [];
$headers = [];
$itemName = '';
$ordersHasSample = [];
/** @var BuyOrder $order */
foreach ($orderList as $order) {
foreach ($order->getArticles() as $orderItem) {
// для списка заказов в которые артикул был уже добавлен
if ($orderItem->getArticle() && $orderItem->getArticle()->getId() == $itemId) {
$active[$order->getHash()] = true;
}
// если добавляется образец, надо проверить его на совпадение
if ($type == BuyOrderArticle::TYPE_SAMPLE) {
$itemAdd = $this->articleRepository->getArticle($itemId);
if ($itemAdd) {
$itemName = $itemAdd->getName();
// получаем данные по образцу для сравнения
$sampleData = $this->sampleService->getSampleData(
$itemAdd->getCode(),
$order->getDeliveryCountry()->getId(),
(bool) $order->getVatType(),
$order->getCurrency(),
$order->getMeasure()
);
if ($sampleData) {
if (
$res = $this->checkingDoubleSample(
$orderItem,
$itemId,
(string) $sampleData['code']
)
) {
$ordersHasSample[] = $res;
}
}
}
} else {
break;
}
}
$headers[$order->getId()] = strip_tags(OrderHelper::header($order, true));
}
$response = [
'html' => $this->render(
'@Web/BuyOrder/list.html.twig',
[
'articleId' => $itemId,
'orders' => $orderList,
'active' => $active,
'itemCount' => $itemCount,
'headers' => $headers,
'type' => $type,
]
),
];
if (count($ordersHasSample) > 0) {
$response['msg'] = [
'header' => $this->translationService->trans(
'sample_already_added.header',
App::getCurLocale(),
[
'%name%' => $itemName,
]
),
'body' => $this->translationService->trans(
'article.in_orders',
App::getCurLocale(),
[
'%d%' => '"' . join('", "', $ordersHasSample) . '"',
]
),
];
}
}
return $response;
}
/**
* @param BuyOrderArticle $item
* @param int $id
* @param string $code
* @return false|string
* @throws Exception
*/
public function checkingDoubleSample(BuyOrderArticle $item, int $id, string $code)
{
if ($item->getArticle()->getId() != $id && $item->chooseCode() == $code) {
return ('<a href="' .
App::generateUrl('app_buy_order', ['orderId' => $item->getBuyOrder()->getHash()]) .
'" target="_blank">' . OrderHelper::humanName(
$item->getBuyOrder()->getName(),
$item->getBuyOrder()->getStatus(),
$item->getBuyOrder()->getBankTransferRequested()
) . '</a>');
} else {
return false;
}
}
/**
* Подсчет зарезервированных образцов по всем активным заказам
*
* @param string $sampleCode Код образца
* @param string|null $excludeOrderHash Исключить заказ с данным хешем из подсчета
* @return int Количество зарезервированных образцов
*/
public function getReservedSampleCount(string $sampleCode, ?string $excludeOrderHash = null): int
{
// Статусы активных заказов: 1 = REQUEST_DELIVERY, 2 = AWAITING_PAYMENT, 3 = ?
$activeStatuses = [1, 2, 3];
$qb = $this->em->createQueryBuilder();
$qb->select('SUM(boa.amount) as total')
->from('WebBundle:BuyOrder', 'bo')
->join('bo.articles', 'boa')
->where('bo.status IN (:statuses)')
->andWhere('boa.type = :sampleType')
->andWhere('boa.sampleCode = :sampleCode')
->setParameter('statuses', $activeStatuses)
->setParameter('sampleType', BuyOrderArticle::TYPE_SAMPLE)
->setParameter('sampleCode', $sampleCode);
if ($excludeOrderHash !== null) {
$qb->andWhere('bo.hash != :excludeHash')
->setParameter('excludeHash', $excludeOrderHash);
}
$result = $qb->getQuery()->getSingleScalarResult();
return (int) ($result ?: 0);
}
/**
* Переотправка заказа в 1С, сброс: если уже был отправлен
*/
public function recreateOrderTo1C(string $hash): bool
{
$order = $this->getOrderObj($hash);
if ($order->getSendCreate1C()) {
$this->log('RESETORDERONEC', $order->getHash(), 'resend order by admin');
$this->oneCService->resetOrderOneC($order, 'resend order by admin');
} else {
$this->log('RESETORDERSITE', $order->getHash(), 'resend order-SITE by admin');
}
$result = $this->oneCService->createOrderOneC(App::getRequest(), $order);
return $result['Result'] && empty($result['Result']['Error']);
}
/**
* Синхронизация заказа с "1C"
*
* @param string $hash
* @return bool
* @throws Exception
*/
public function syncOrder(string $hash): bool
{
$order = $this->getOrderObj($hash);
// сохраняем заказ перед изменением
$this->oldOrder = clone $order;
$result = $this->oneCService->getOrder($hash);
if (empty($result['Result'])) {
throw new Exception('Fail decode json');
}
if (!empty($result['NumberOrder'])) {
$order->setNumber($result['NumberOrder']);
}
if (!empty($result['currency'])) {
$order->setCurrency($result['currency']);
}
$this->setManagerToOrder($order, $result['manager'] ?? null);
if (!empty($result['measure'])) {
$order->setMeasure(MeasureOrderEnum::M);
if ($result['measure'] == MeasureOrderEnum::FT) {
$order->setMeasure(MeasureOrderEnum::FT);
}
}
if (!empty($result['vat'])) {
$vat = $result['vat'];
if (isset($vat['type'])) {
$order->setVatType((int) $vat['type']);
}
if (!empty($vat['number'])) {
$order->setVatNumber($vat['number']);
}
}
if (!empty($result['delivery'])) {
$delivery = $result['delivery'];
if (!empty($delivery['country'])) {
/** @var ListCountry $countryEntity */
$countryEntity = $this->em->getReference('WebBundle\Entity\ListCountry', (int) $delivery['country']);
$order->setDeliveryCountry($countryEntity);
// Чистим индекс для России если была до этого другая страна и индекс имел буквы
if ($delivery['country'] == 643 && !empty($delivery['index']) && !is_int($delivery['index'])) {
$delivery['index'] = null;
$order->setDeliveryIndex(null);
}
}
if (!empty($delivery['city'])) {
$order->setDeliveryCity($delivery['city']);
}
if (isset($delivery['lift'])) {
if (BuyOrderLiftEnum::isValid((int) $delivery['lift'])) {
$order->setLift((int) $delivery['lift']);
} else {
$order->setLift(BuyOrderLiftEnum::UNDEFINED);
}
}
if (!empty($delivery['index'])) {
$order->setDeliveryIndex($delivery['index']);
}
if (!empty($delivery['region'])) {
$order->setDeliveryRegion($delivery['region']);
}
if (!empty($delivery['street'])) {
$order->setDeliveryAddress($delivery['street']);
}
if (!empty($delivery['building'])) {
$order->setDeliveryBuilding($delivery['building']);
}
if (!empty($delivery['apartmentOffice'])) {
$order->setDeliveryApartmentOffice($delivery['apartmentOffice']);
}
if (!empty($delivery['price'])) {
$order->setDelivery(PriceHelper::formatPrice($delivery['price']));
}
if (!empty($delivery['weightReport'])) {
$order->setWeightReport((float) str_replace(',', '.', $delivery['weightReport']));
}
OrderHelper::updateWeightGrossForOrderInNeeded($order, $delivery['weightGross'] ?? null);
if (isset($delivery['carrierName'])) {
$order->setCarrierName(strip_tags($delivery['carrierName']));
}
if (isset($delivery['deliveryType'])) {
$order->setDeliveryType($delivery['deliveryType'] == 1 ? 1 : 0);
}
if (!empty($delivery['dateExecution'])) {
$order->setPayDateExecution(DateHelper::standard($delivery['dateExecution']));
}
if (!empty($delivery['timeMin'])) {
$order->setDeliveryTimeMin(DateHelper::standard($delivery['timeMin']));
}
if (!empty($delivery['timeMax'])) {
$order->setDeliveryTimeMax(DateHelper::standard($delivery['timeMax']));
}
if (!empty($delivery['timeConfirmMin'])) {
$order->setDeliveryTimeConfirmMin(DateHelper::standard($delivery['timeConfirmMin']));
}
if (!empty($delivery['timeConfirmMax'])) {
$order->setDeliveryTimeConfirmMax(DateHelper::standard($delivery['timeConfirmMax']));
}
// если заказ оплачен и срок сдвинули в большую сторону надо отправлять сообщение об этом
if (
$order->getStatus() > BuyOrderStatusEnum::PAID_NOT_CONFIRMED
&& (
$this->oldOrder
&& $this->oldOrder->getDeliveryTimeMin()
&& $this->oldOrder->getDeliveryTimeMax()
&& $order->getDeliveryTimeMin()
&& $order->getDeliveryTimeMax()
&& (
$this->oldOrder->getDeliveryTimeMin()->getTimestamp() < $order->getDeliveryTimeMin()->getTimestamp()
|| $this->oldOrder->getDeliveryTimeMax()->getTimestamp() < $order->getDeliveryTimeMax()->getTimestamp()
)
)
) {
$this->historyService->saveHistoryAndSendEmail(
$order,
'confirm_change_time_shipment',
OrderHistoryService::SEND_EMAIL
);
}
}
$rItems = $result['articles'];
/** @var BuyOrderArticle $item */
foreach ($order->getArticles() as $item) {
$item->setArticleTimeType(
$item->getArticleTimeType() ?? DeliveryTypeTimeEnum::CALCULATED
);
if (empty($rItems[$item->chooseCode()])) {
$this->historyService->saveHistoryAndSendEmail(
$order,
'event.item_deleted',
OrderHistoryService::NO_SEND_EMAIL,
[
'%name%' => $item->getArticle()->getName(),
],
1
);
$this->em->remove($item);
$this->em->flush();
continue;
}
$a = $rItems[$item->chooseCode()];
$amount = round(PriceHelper::formatPrice($a['amount']), 5);
$price = PriceHelper::formatPrice(!empty($a['Minprice']) ? $a['Minprice'] : $a['price']);
$priceEuro = PriceHelper::formatPrice(!empty($a['MinpriceEuro']) ? $a['MinpriceEuro'] : $a['priceEuro']);
if (!empty($a['sum'])) {
$price = round(LocaleHelper::getFloatFromString($a['sum']) / $amount, 2);
$priceEuro = round(PriceHelper::formatPrice($a['sumEuro']) / $amount, 2);
}
if (!empty($rItem['type'])) {
$item->setType($rItem['type']);
}
if (!empty($rItem['sample_format'])) {
$item->setSampleFormat($rItem['sample_format']);
}
if (!empty($rItem['sample_type'])) {
$item->setSampleType((int) $rItem['sample_type']);
}
if (!empty($rItem['priceEuro'])) {
$item->setPriceEUR($priceEuro);
}
if ($item->getType() == BuyOrderArticle::TYPE_SAMPLE) {
$amount = ceil($amount);
}
$item->setAmount($amount);
$item->setPrice($price);
$this->setEuroForRussia($order);
$item->setCurrency($order->getCurrency());
$item->setMeasure($order->getMeasure());
$this->setDateItemSync($order, $item, $a);
unset($rItems[$item->chooseCode()]);
$this->em->persist($order);
$this->em->persist($item);
}
$this->em->flush();
$codes = $this->articleRepository->getArticlesForCodes(array_keys($rItems));
foreach ($rItems as $key => $rItem) {
if (!array_key_exists($key, $codes)) {
continue;
}
$item = $codes[$key];
$amount = round(PriceHelper::formatPrice($rItem['amount']), 5);
$price = PriceHelper::formatPrice(!empty($rItem['Minprice']) ? $rItem['Minprice'] : $rItem['price']);
$priceEuro = PriceHelper::formatPrice(!empty($rItem['MinpriceEuro']) ? $rItem['MinpriceEuro'] : $rItem['priceEuro']);
$orderItem = $this->itemService->createOrderItem(
$item,
empty($rItem['type']) ? 1 : $rItem['type'],
$order
);
if ($orderItem->getType() == BuyOrderArticle::TYPE_SAMPLE) {
$amount = ceil($amount);
}
$orderItem->setAmount($amount);
$orderItem->setPrice($price);
$orderItem->setPriceEUR($priceEuro);
if (!empty($rItem['sample_format'])) {
$orderItem->setSampleFormat($rItem['sample_format']);
}
if (!empty($rItem['sample_type'])) {
$orderItem->setSampleType((int) $rItem['sample_type']);
}
$orderItem->setWeight($item->getWeight());
$orderItem->setPackagingCount(
LocaleHelper::getPackagingCount(
$item,
OrderHelper::params($order, [
'measureId' => $item->getMeasure()->getId(),
'measure' => $orderItem->getMeasure(),
]),
$orderItem->getType()
)
);
$orderItem->setPackaging(
$orderItem->getType() == BuyOrderArticle::TYPE_SAMPLE ? 1 : $item->getPackagingCount()
);
$orderItem->setBuyOrder($order);
$this->setEuroForRussia($order);
$orderItem->setCurrency($order->getCurrency());
$orderItem->setMeasure($order->getMeasure());
$this->setDateItemSync($order, $orderItem, $rItem);
$this->historyService->saveHistoryAndSendEmail(
$order,
'event.item_added_sync',
OrderHistoryService::NO_SEND_EMAIL,
[
'%name%' => $orderItem->getArticle()->getName(),
'%amountOld%' => 0,
'%amount%' => $amount,
'%price%' => $price,
'%currency%' => $orderItem->getCurrency(),
'%measure%' => $orderItem->getMeasure(),
],
1
);
$order->addArticle($orderItem);
$this->em->persist($order);
$this->em->persist($orderItem);
}
$this->em->flush();
if (!empty($result['Status'])) {
$this->buyOrderRepository->save($order);
if (
$result['Status'] == BuyOrderStatusEnum::AWAITING_CALCULATION
&& $order->getDelivery() !== null
&& $order->getDeliveryTimeMin() !== null
) {
$result['Status'] = BuyOrderStatusEnum::AWAITING_PAYMENT;
}
$this->setStatus($order, $result['Status'], [
'fromOneC' => true,
'syncFromOneC' => true,
]);
}
$this->buyOrderRepository->save($order);
$this->setUserData($order);
$this->setNewHashOrder($order);
$this->buyOrderRepository->save($order);
return true;
}
/**
* @param BuyOrder $order
* @param BuyOrderArticle $item
* @param array $a
* @throws Exception
*/
private function setDateItemSync(BuyOrder $order, BuyOrderArticle $item, array $a)
{
if ($order->getPayDateExecution() && !empty($a['timeMin'])) {
if ($timeMin = DateHelper::standard($a['timeMin'])) {
$item->setDeliveryTimeMin($timeMin);
} else {
$date = new DateTime($order->getPayDateExecution()->format('Y-m-d'));
$date->modify('+' . $a['timeMin'] . ' day');
$item->setDeliveryTimeMin($date);
}
}
if ($order->getPayDateExecution() && !empty($a['timeMax'])) {
if ($timeMax = DateHelper::standard($a['timeMax'])) {
$item->setDeliveryTimeMax($timeMax);
} else {
$date = new DateTime($order->getPayDateExecution()->format('Y-m-d'));
$date->modify('+' . $a['timeMax'] . ' day');
$item->setDeliveryTimeMax($date);
}
}
if ($order->getPayDateExecution() && !empty($a['timeConfirmMin'])) {
if ($timeConfirmMin = DateHelper::standard($a['timeConfirmMin'])) {
$item->setDeliveryTimeConfirmMin($timeConfirmMin);
} else {
$date = new DateTime($order->getPayDateExecution()->format('Y-m-d'));
$date->modify('+' . $a['timeConfirmMin'] . ' day');
$item->setDeliveryTimeConfirmMin($date);
}
}
if ($order->getPayDateExecution() && !empty($a['timeConfirmMax'])) {
if ($timeConfirmMax = DateHelper::standard($a['timeConfirmMax'])) {
$item->setDeliveryTimeConfirmMax($timeConfirmMax);
} else {
$date = new DateTime($order->getPayDateExecution()->format('Y-m-d'));
$date->modify('+' . $a['timeConfirmMax'] . ' day');
$item->setDeliveryTimeConfirmMax($date);
}
}
}
/**
* @param BuyOrder $order
* @throws Exception
*/
private function setUserData(BuyOrder $order)
{
//создание или обновление пользователя
if (
empty($data['checkHash'])
&& $order->getEmail()
&& (
$order->getUser() == null
|| $order->getVatType() == 1
&& $order->getVatNumber()
&& $order->getUnidVatPlayer() == null
|| OrderHelper::isChangeDataUser($order, $this->oldOrder)
)
) {
try {
$user = $this->orderUserService->resolve(App::getRequest(), $order);
$order->setUser($user);
if ($order->getAddressRecipient() && !$order->getAddressRecipient()->getUser()) {
$order->getAddressRecipient()->setUser($user);
}
if ($user && $user->getCorporateUnid()) {
$order->setUnidVatPlayer($user->getCorporateUnid());
}
} catch (EmailAlreadyRegisteredException $e) {
$msg = $this->translationService->trans('buyOrder.fields.error.email_exist', App::getCurLocale(), [
'%login%' => App::generateUrl('app_login', ['_locale' => App::getCurLocale(true)]),
]);
$this->errors[] = $msg;
}
}
}
/**
* @param BuyOrder $order
* @return bool
* @deprecated
*/
public function mayChange(BuyOrder $order): bool
{
return ($order->getStatus() && $order->getStatus() == BuyOrderStatusEnum::AWAITING_CALCULATION);
}
/**
* Формирует массив стран куда возможен заказ
* @param BuyOrder $order
* @return array
* @throws Exception
* @deprecated
*/
public function getCountry(BuyOrder $order): array
{
$countryList = $this->listCountryRepository->getListForByOrder();
$countryAuto = [];
$cnt = [
'auto' => [],
'noAuto' => [],
];
$country = [
'regions' => [],
'regionShow' => false,
];
foreach ($countryList as $c) {
if (!empty($c['code']) && !empty($c['alias'])) {
$auto = in_array($c['id'], $countryAuto) ? 'auto' : 'noAuto';
$cnt[$auto][strtolower($c['code'])] = [
// alias обязательно первый, так как по нему сортировка работает
'alias' => $this->translationService->trans($c['alias']),
'id' => $c['id'],
'payPalPercent' => $c['payPalPercent'],
'payPalPercentOwn' => $c['payPalPercentOwn'],
'phoneCode' => $c['phoneCode'],
'phoneMask' => $c['phoneMask'],
'zipCodeMask' => $c['zipCodeMask'],
'minValidPhoneNumbers' => $c['minValidPhoneNumbers'],
'strong' => ($auto == 'auto'),
];
if (!empty($c['states'])) {
$region = [];
foreach ($c['states'] as $r) {
$a = [
'code' => $r['code'],
'name' => $r['name'],
'countryId' => $c['id'],
];
if (
$order->getDeliveryCountry() &&
$order->getDeliveryCountry()->getId() == $c['id'] ||
$order->getDeliveryCountry() == null &&
App::getCurCountry() == strtolower($c['code'])
) {
$country['regionShow'] = true;
$a['show'] = true;
// определяем выбранный регион доставки если поле уже заполнено, только в том случае,
// если регион выбранный в заказе соответствует выбранной стране
if (
$order->getDeliveryRegion() &&
in_array($order->getDeliveryRegion(), [$r['code'], strtolower($r['name'])])
) {
$a['sel'] = true;
}
}
$region[] = $a;
}
$country['regions'] = array_merge($country['regions'], $region);
}
}
}
// если есть страны по которым возможен авто расчет
if (count($cnt['auto']) > 0) {
$country['0'] = [
'alias' => 'buyOrder.info.online_calculation',
'class' => 'selectBox-optgroup',
];
asort($cnt['auto']);
$country = array_merge($country, $cnt['auto']);
$country['1'] = [
'alias' => 'buyOrder.info.request_calculation',
'class' => 'selectBox-optgroup',
];
}
asort($cnt['noAuto']);
$country = array_merge($country, $cnt['noAuto']);
foreach (OrderHelper::noEU()['char'] as $row) {
if (isset($country[$row])) {
$country[$row]['noEU'] = 1;
}
}
foreach (OrderHelper::vatRequired()['char'] as $row) {
if (isset($country[$row])) {
$country[$row]['VatRequired'] = 1;
}
}
if ($order->getDeliveryCountry() != null && $order->getDeliveryCountry()->getCode() != null) {
$country[strtolower($order->getDeliveryCountry()->getCode())]['selected'] = true;
} else {
$uCountry = strtolower(LocaleHelper::getUserCountry());
if (isset($country[$uCountry])) {
$country[$uCountry]['selected'] = true;
} else {
$country[strtolower(substr(App::getCurLocale(true), -2, 2))]['selected'] = true;
}
}
// в заказе не должен вариант en отображаться
unset($country['en']);
return $country;
}
/**
* Подтверждение изменённых сроков
*
* @param string $token
* @param string $orderId
* @param bool $val
* @return mixed
* @throws Exception
* @deprecated
*/
public function confirmTimes(string $token, string $orderId, bool $val)
{
$params = [
'hash' => $orderId,
'status' => BuyOrderStatusEnum::PAID_NOT_CONFIRMED,
];
if (!$this->authorizationChecker->isGranted('ROLE_SUPER_ADMIN')) {
$params['token'] = $token;
}
$order = $this->buyOrderRepository->getBuyOrder($params);
if (null === $order) {
return false;
}
$result = $this->oneCService->confirm($order, $val);
if (!empty($result['dateStock'])) {
$order->setShipDate(DateHelper::standard($result['dateStock']));
}
if (!empty($result['sendingDate'])) {
$order->setSendingDate(DateHelper::standard($result['sendingDate']));
}
$this->setStatus($order, ($val ? BuyOrderStatusEnum::TREKKING_AWAITS : BuyOrderStatusEnum::CANCELLED));
$order = OrderHelper::updateDeliveryTimeTypeAll($order, DeliveryTypeTimeEnum::CONFIRMED());
$this->buyOrderRepository->save($order);
return array_merge(
$result,
[
'status' => [
'id' => $order->getStatus(),
'text' => $this->translationService->trans(BuyOrderStatusEnum::from($order->getStatus())->getAlias()),
],
'id' => $order->getId(),
'delivery' => OrderHelper::time($order),
]
);
}
/**
* Записываем новые хеши данных заказа
* хеш нужно обязательно перестраивать перед обновлением иначе будут дубль запросы в 1С
*
* @param BuyOrder $order
* @return void
* @throws Exception
*/
public function setNewHashOrder(BuyOrder $order): void
{
$order->setMarkerReset(OrderHelper::markerReset($order));
$order->setMarkerChange(OrderHelper::md5MarkerChange($order));
}
/**
* @param BuyOrder $order
* @return array
* @throws Exception
*/
public function getProjectData(BuyOrder $order): array
{
return [
'name' => OrderHelper::humanName(
$order->getName(),
$order->getStatus(),
$order->getBankTransferRequested(),
),
'url' => App::generateUrl('app_buy_order', [
'orderId' => $order->getHash(),
]),
'hash' => $order->getHash(),
'number' => $order->getNumber(),
];
}
public function checkOrderByLink(string $link, User $user): void
{
$path_list = ['buy-order'];
$parts = explode('/', $link);
/**
* order link have the following format http://host/local/Order page/order hash
* So result of explode will have the following structure
* [
* 0 => 'http:',
* 1 => '',
* 2 => 'hast',
* 3 => 'local',
* 4 => 'order path(buy-order|order)',
* 5 => 'order hash',
* ]
*/
if (!in_array(($parts[4] ?? ''), $path_list)) {
return;
}
if (empty($parts[5])) {
return;
}
$order = $this->buyOrderRepository->getBuyOrder(['hash' => $parts[5]]);
if (empty($order)) {
return;
}
$order->setStep(2);
$order->setUser($user);
$order->setEmail($user->getEmail());
$this->em->persist($order);
$this->em->flush();
}
/**
* Фиксируем, что новые сроки подтвержденные
*
* @param BuyOrder $order
* @param bool|null $confirmed
* @return void
* @throws Exception
*/
public function setNewTermsOfDelivery(BuyOrder $order, ?bool $confirmed): void
{
$order->setDeliveryTimeType(DeliveryTypeTimeEnum::CONFIRMED);
// 7 - заказ переводим в ожидающий погрузки, 10 -в отменённый
$this->setStatus($order, $confirmed ? BuyOrderStatusEnum::TREKKING_AWAITS : BuyOrderStatusEnum::CANCELLED);
$this->oneCService->confirm($order, $confirmed);
}
public function setOrderStatusByPayment(OrderPayment $payment): void
{
$order = $payment->getBuyOrder();
$alreadyPayment = OrderHelper::paymentSum($order);
$orderStatus = BuyOrderStatusEnum::AWAITING_PAY_SYS;
if ($payment->getStatus() === PaymentStatusEnum::SUCCESS) {
$orderStatus = BuyOrderStatusEnum::PAID;
if (OrderHelper::totalSum($order) - $alreadyPayment > 0) {
$orderStatus = BuyOrderStatusEnum::PARTIALLY_PAID;
}
}
$this->setStatus($order, $orderStatus);
$this->buyOrderRepository->save($order);
}
/**
* @param bool $file64
* @param int $oldStatus
* @param int $status
* @return bool false - Обновляем статус заказа, true - не обновляем
*/
private function dontChangeOrderStatus(bool $file64, int $oldStatus, int $status): bool
{
if ($status === BuyOrderStatusEnum::PARTIALLY_DELIVERED) {
return false;
}
if ($status === BuyOrderStatusEnum::DELIVERED && $file64) {
return false;
}
if ($oldStatus === BuyOrderStatusEnum::EXPORT && $status === BuyOrderStatusEnum::DELIVERED) {
return false;
}
if (
($file64
|| $oldStatus !== $status
|| $status === BuyOrderStatusEnum::PARTIALLY_PAID
)
&& ($oldStatus <= BuyOrderStatusEnum::PARTIALLY_PAID || $status >= BuyOrderStatusEnum::PARTIALLY_PAID)
&& $status !== BuyOrderStatusEnum::DELIVERED
) {
return false;
}
return true;
}
/**
* Добавим платёж в заказ, если необходимо
* Если передана транзакция, то оплата уже существует в нашей системе, создавать её не нужно
* Если передан статус и статус - запрос возврата (23), то не записываем его как транзакцию
*/
private function addPaymentForOrder(array $data, BuyOrder $order): void
{
$data['IDTransaction'] ??= $data['IDTransaction '] ?? null;
if (!empty($data['IDTransaction'])) {
return;
}
if (!empty($data['status']) && ((int) $data['status']) === BuyOrderStatusEnum::REFUND_OF_FUNDS) {
return;
}
$sum = $this->getSum($data);
$type = $this->getPaymentTypeForOrder($data);
$order->setPayType($type);
$payment = $this->paymentService->addPayment(
$order,
[
'status' => 1,
'amount' => $sum,
'paySys' => $type,
'curRate' => $data['curRate'] ?? null,
'currency' => empty($data['sumCurrency']) ? $order->getCurrency() : $data['sumCurrency'],
'percent' => OrderHelper::paymentPercent($order, $type),
]
);
$order->addPayment($payment);
}
private function getSum(array $data): float
{
if (!empty($data['sum'])) {
return LocaleHelper::getFloatFromString($data['sum']);
}
return -1 * LocaleHelper::getFloatFromString($data['sumBack']);
}
/**
* @param array $data
* @param BuyOrder $order
* @throws Exception
*/
private function checkSumOrSumBackData(array $data, BuyOrder $order): void
{
// sumBack для возвратов
if (empty($data['sum']) && empty($data['sumBack'])) {
return;
}
$this->addPaymentForOrder($data, $order);
// записываем в историю запись о возврате и отправляем письмо
if (
!empty($data['sumBack'])
&& empty($data['operation'])
&& $order->getStatus() != BuyOrderStatusEnum::PARTIALLY_PAID
) {
$this->historyService->saveHistoryAndSendEmail(
$order,
'back_payment',
OrderHistoryService::SEND_EMAIL
);
}
}
/**
* @param array $data
* @return int
*/
private function getPaymentTypeForOrder(array $data): int
{
$type = $this->oldOrder->getPayType();
if (empty($type)) {
$type = PaymentTypeEnum::getBankTransferTypeIfPayTypeNotAvailable($data['sumType'] ?? null);
}
if (!empty($data['method']) && PaymentTypeEnum::isAvailableMethod((int) $data['method'])) {
$type = (int) $data['method'];
}
return PaymentTypeEnum::changeOldPayPalToNew($type);
}
/**
* Отправляем в 1С заказ если он готов
* отправляем все изменения заказа
*
* @param BuyOrder $order
* @param array $data
* @return array
* @throws SoapFault
*/
private function sendOrderTo1CIfItReady(BuyOrder $order, array $data): array
{
if (
empty($data['checkHash'])
&& $order->getStatus() < BuyOrderStatusEnum::PARTIALLY_PAID
&& OrderHelper::readyCreate($order)
) {
$result = $this->oneCService->createOrderOneC(App::getRequest(), $order, false, $this->timer);
$data = array_merge($data, $result);
}
return $data;
}
private function orderAvailableToChange(array $data, BuyOrder $order): bool
{
if (empty($data)) {
return false;
}
if ($order->getStatus() === BuyOrderStatusEnum::DELETED) {
return false;
}
return true;
}
/**
* Сохраняем заказ перед изменением
*/
private function cloneOrderIfNeeded(BuyOrder $order): void
{
if (null === $this->oldOrder) {
$this->oldOrder = clone $order;
}
}
private function setManagerToOrder(BuyOrder $order, ?array $manager): void
{
$orderManager = $this->buyOrderManagerService->getBuyOrderManager(
$manager['email'] ?? null,
$manager['login'] ?? null,
$manager['name'] ?? null,
$manager['phone'] ?? null,
);
if ($orderManager) {
$order->setBuyOrderManager($orderManager);
}
}
public function getPaymentOrdersByDate(?DateTime $start, ?DateTime $end): array
{
if (null === $start) {
$start = (new DateTime('now'))->modify('-3 day');
}
if (null === $end) {
$end = new DateTime('now');
}
return $this->buyOrderRepository->paymentOrder($start->format('Y-m-d'), $end->format('Y-m-d'));
}
/**
* Этот метод меняет токены юзера у заказа и проставляет юзерИД в заказы с токеном $token
*/
public function changeUserInOrders(User $user, string $token): void
{
$orders = $this->buyOrderRepository->findByToken($token);
foreach ($orders as $order) {
if ($order->getUser() !== null) {
continue;
}
$order->setUser($user);
$order->setToken($user->getToken());
$order->setEmail($user->getEmail());
$this->buyOrderRepository->save($order, false);
}
$this->buyOrderRepository->flush();
}
/**
* Для России, в случае, если заказ образцов, то валюту ставим Euro
*/
private function setEuroForRussia(BuyOrder $order): void
{
if (
OrderHelper::onlySamples($order)
&& $order->getDeliveryCountry()
&& $order->getDeliveryCountry()->getId() == 643
&& $order->getCurrency() != 'EUR'
) {
$order->setCurrency('EUR');
}
}
/**
* Обновляем данные и тип артикулов, только если заказ не оплачен либо запрос от "1С"
*
* @throws Exception
*/
private function updateItemsForOrder(array $data, BuyOrder $order, BuyOrder $oldOrder, bool $isRequestFrom1C): array
{
if (!$isRequestFrom1C && $order->getStatus() >= BuyOrderStatusEnum::PARTIALLY_PAID) {
return $data;
}
// Изменение логики удаления артов из заказа - теперь если items не совпадает с артами заказа - обновим
// Но! остаётся вопрос про последний арт - в этом случае не удалим, в будущем (на новом заказе) фронт
// обещает всегда items - соответственно перенаполнение его здесь только для 1С сделать
// Если менялась система исчисления или валюта надо проверить, что все артикулы переданы
if (
empty($data['items'])
|| $oldOrder->getMeasure() != $order->getMeasure()
|| $oldOrder->getCurrency() != $order->getCurrency()
) {
$data = OrderHelper::itemsArr($order, $data);
}
if ($this->itemService->updateItems($order, $oldOrder, $data['items'], $isRequestFrom1C)) {
$this->buyOrderRepository->save($order);
}
return $data;
}
/**
* @param BuyOrder $order
* @return float
*/
private function getOrderBalance(BuyOrder $order): float
{
$paymentSum = OrderHelper::paymentSum($order);
$totalSum = OrderHelper::totalSum($order);
return $totalSum - $paymentSum;
}
/**
* @param BuyOrder $order
* @param array $data
* @return bool
* @throws Exception
*/
private function isValidTaxNumber(BuyOrder $order, array $data): bool
{
$pattern = VatTypeEnum::INDIVIDUAL === (int) ($data['vat']['type'] ?? VatTypeEnum::INDIVIDUAL)
? '/^(([a-z][0-9]{7}[a-z])|([a-z][0-9]{8})|([0-9]{8}[a-z]))$/i'
: '/^[a-z][0-9]{8}$/i';
if (1 !== preg_match($pattern, str_replace(' ', '', $data['taxNumber']))) {
$errors = [
'taxNumber' => 'Spain tax number has an invalid format.',
];
$this->setError($errors['taxNumber']);
$this->log(
'SPAIN_TAX_CODE_IS_NOT_VALID',
$order->getHash(),
$errors
);
return false;
}
return true;
}
private function findUserByEmail(string $email): ?User
{
$userRepository = $this->em->getRepository(User::class);
return $userRepository->findOneBy(['email' => $email]);
}
}