getOrderList(); $orderList->andSpec(Operator::equals(['o.id' => $id])); $orders = $orderList->getOrders(); if (!($order = $orders->current())) { throw new GraphQLNotFoundException('Order not found'); } $order->productList->fetchPhotos('product_gallery', ['Y', 'N']); return new Order($order, $orders); } public function getOrders(?int $offset, int $limit, ?Parameters $sort, ?Parameters $filter): OrderCollection { $sortValues = $sort ? array_filter($sort->getData()) : []; $filterValues = $filter ? array_filter($filter->getData()) : []; $orderList = clone $this->orderList; // limit $orderList->limit(ApiUtil::getLimit($limit), $offset); // sorting foreach ($sortValues as $field => $sortValue) { $orderList->andSpec(function (QueryBuilder $qb) use ($field, $sortValue) { $qb->addOrderBy($field, $sortValue->value); }); } if ($specs = $this->getOrdersFilterSpecs($filterValues)) { $orderList->andSpec(Operator::andX($specs)); } return (new OrderCollection($orderList->getOrders($totalCount))) ->setItemsTotalCount($totalCount ?? 0) ->setLimit($limit) ->setOffset($offset); } public function stornoOrder(OrderStornoInput $input): OrderMutateResponse { try { $order = \Order::get($input->id); } catch (\InvalidArgumentException $e) { throw new GraphQLNotFoundException($e->getMessage()); } if ($order->status_storno == 1) { throw new GraphQLValidationException('Cannot cancel order! Order is already cancelled.'); } $stornoHistoryMessage = '[API] Stornování objednávky.'; if ($input->message) { $stornoHistoryMessage .= 'Důvod: '.$input->message; } $order->logHistory($stornoHistoryMessage); $order->storno(false, $input->message, $input->sendMail); return new OrderMutateResponse(true, $this->getOrder($input->id)); } public function updateOrder(OrderUpdateInput $input): OrderMutateResponse { try { $order = \Order::get($input->id); } catch (\InvalidArgumentException $e) { throw new GraphQLNotFoundException($e->getMessage()); } $updateLog = []; // aktualizace polozek objednavky if (ObjectUtil::isPropertyInitialized($input, 'items')) { foreach ($input->items ?: [] as $item) { if (!ObjectUtil::isPropertyInitialized($item, 'id') || empty($item->id)) { throw new GraphQLValidationException( 'OrderUpdateInput.items.id: Item ID is mandatory when updating order items!' ); } /** @var OrderItem $orderItem */ $orderItem = $order->getItems()[$item->id] ?? false; if (!$orderItem) { throw new GraphQLValidationException( sprintf('OrderUpdateInput.items: Item with ID "%s" is not included in the order!', $item->id) ); } if (ObjectUtil::isPropertyInitialized($item, 'delete')) { if ($item->delete === true) { $order->deleteItem($item->id); $updateLog[] = "Smazání položky \"{$item->id}\": {$orderItem->getDescr()}; {$orderItem->getPieces()} ks"; continue; } } if (ObjectUtil::isPropertyInitialized($item, 'quantity')) { if ($item->quantity < 0) { throw new GraphQLValidationException( 'OrderUpdateInput.items.quantity: Quantity cannot be negative value!' ); } // aktualizace poctu kusu u polozky if ($orderItem->getPieces() != $item->quantity) { $order->updateItem($item->id, $item->quantity); $updateLog[] = "Aktualizace položky \"{$item->id}\": {$orderItem->getDescr()}; {$orderItem->getPieces()} -> {$item->quantity} ks"; } } } // smazani items cache, aby se pripadne polozky prenacetly podle aktualniho stavu $order->items = []; } // aktualizace cisla baliku if (ObjectUtil::isPropertyInitialized($input, 'packageId')) { $order->setPackageId($input->packageId); $updateLog[] = "Nastaveno číslo balíku: {$input->packageId}"; } // custom faktura - TODO: odebrat az si to na sartoru zmeni if (ObjectUtil::isPropertyInitialized($input, 'invoiceUrl') && !ObjectUtil::isPropertyInitialized($input, 'documents')) { $document = new OrderDocument(); $document->type = OrderDocumentType::from('order_invoice'); $document->url = $input->invoiceUrl; $input->documents = [$document]; } // custom doklady if (ObjectUtil::isPropertyInitialized($input, 'documents')) { $externalDocuments = $order->getData('externalDocuments') ?: []; foreach ($input->documents ?: [] as $document) { $externalDocuments[$document->type->getValue()] = $document->url; $updateLog[] = sprintf('Nastaven doklad %s: %2$s', $document->type->getValue(), $document->url); } $order->setData('externalDocuments', $externalDocuments); } if (ObjectUtil::isPropertyInitialized($input, 'userId')) { if (!$this->checkUserExists($input->userId)) { throw new GraphQLNotFoundException(sprintf('User with ID %s was not found!', $input->userId)); } $order->assignUser($input->userId); } // zmena stavu objednavky if (ObjectUtil::isPropertyInitialized($input, 'status') && $input->status) { if (!(getOrderStatuses()[$input->status->id] ?? false)) { throw new GraphQLNotFoundException( sprintf('Status with ID "%s" was not found!', $input->status->id) ); } if ($input->status->comment) { $order->logHistory($input->status->comment); } if ($input->status->emailType) { try { $this->emailLocator->getEmailService($input->status->emailType); } catch (UnknownEmailTypeException) { throw new GraphQLValidationException( sprintf('Email type "%s" does not exists!', $input->status->emailType) ); } } if (!$input->status->userMessage) { $input->status->userMessage = $this->getOrderMessageByStatus($input->status->id); } if (ObjectUtil::isPropertyInitialized($input->status, 'sendEmail')) { $sendEmail = $input->status->sendEmail; } $order->changeStatus( $input->status->id, null, $sendEmail ?? null, $input->status->userMessage, $input->status->emailType, ); } // zaloguju info o zmenach, ktere byly behem API volani provedeny if (!empty($updateLog)) { $order->logHistory('[API] Byly provedeny následující aktualizace:
'.implode('
', array_map(fn ($x) => "- {$x}", $updateLog))); } return new OrderMutateResponse(true, $this->getOrder($input->id)); } private function getOrderMessageByStatus(int $statusId): ?string { $email = sqlQueryBuilder() ->select('name') ->from('emails') ->where( Operator::equals( [ 'enabled' => 'Y', 'type' => OrderMessageEmail::getType(), 'order_status' => $statusId, ] ) )->execute()->fetchOne(); if ($email) { return $email; } return null; } public function getOrdersFilterSpecs(array $filterValues): ?callable { $specs = []; $filter = []; if ($filterValues['id'] ?? false) { $specs[] = Operator::inIntArray((array) $filterValues['id'], 'o.id'); } if ($filterValues['code'] ?? false) { $codeFilter = (array) $filterValues['code']; if (count($codeFilter) === 1) { $specs[] = Operator::equals(['o.order_no' => reset($codeFilter)]); } else { $specs[] = Operator::inIntArray((array) $filterValues['code'], 'o.order_no'); } } if (!empty($filterValues['dateCreated'])) { $specs[] = ApiUtil::getDateTimeFilter($filterValues['dateCreated'], 'o.date_created'); } if (!empty($filterValues['dateUpdated'])) { $specs[] = ApiUtil::getDateTimeFilter($filterValues['dateUpdated'], 'o.date_updated'); } if ($filterValues['status'] ?? false) { $filter['statuses'] = (array) $filterValues['status']; } if ($filterValues['userId'] ?? false) { $filter['reg_users'] = (array) $filterValues['userId']; } if ($filterValues['deliveryId'] ?? false) { $filter['deliveries'] = (array) $filterValues['deliveryId']; } if ($filterValues['paymentId'] ?? false) { $filter['payments'] = (array) $filterValues['paymentId']; } if ($filterValues['sellerId'] ?? false) { $filter['sellers'] = (array) $filterValues['sellerId']; } if ($filterValues['source'] ?? false) { $filter['source'] = (array) $filterValues['source']; } if ($filterValues['dropshipId'] ?? false) { $filter['dropship'] = (array) $filterValues['dropshipId']; } // deprecated - just backward compatibility if (($filterValues['date_created']['from'] ?? false) || ($filterValues['date_created']['to'] ?? false)) { $from = ApiUtil::prepareDateTimeForDB($filterValues['date_created']['from'] ?? null); $to = ApiUtil::prepareDateTimeForDB($filterValues['date_created']['to'] ?? null); $specs[] = Operator::between( 'o.date_created', new \Range($from, $to) ); } return $this->ordersFilterSpecs->getSpecs($filter, $specs); } private function getOrderList(): OrderList { return clone $this->orderList; } /** * @required */ public function setOrderList(OrderList $orderList): void { $this->orderList = $orderList; } public function createOrder(OrderCreateInput $orderCreateInput, array $fetchOptions = []): OrderMutateResponse { $this->validateOrderCreateInput($orderCreateInput); return sqlGetConnection()->transactional(function () use ($orderCreateInput, $fetchOptions) { return $this->contextManager->activateContexts([CountryContext::class => $orderCreateInput->deliveryAddress?->country ?? $orderCreateInput->invoiceAddress->country], function () use ($orderCreateInput, $fetchOptions) { $purchaseCalculateInput = new PurchaseCalculateInput(); $purchaseCalculateInput->items = $orderCreateInput->items; $purchaseCalculateInput->coupons = $orderCreateInput->coupons ?? []; $purchaseStateInfo = $this->graphqlPurchaseUtil->calculatePurchaseState($purchaseCalculateInput); $purchaseState = $purchaseStateInfo->getPurchaseState() ?? new PurchaseState([]); $languageContext = Contexts::get(LanguageContext::class); $currencyContext = Contexts::get(CurrencyContext::class); $flags = array_intersect($orderCreateInput->flags ?? [], array_keys(Config::get()['Order']['Flags'])); $orderData = [ 'source' => OrderInfo::ORDER_SOURCE_API, 'note_invoice' => $orderCreateInput->invoice->note ?? null, 'date_created' => ApiUtil::prepareDateTimeForDB($orderCreateInput->dateCreated ?: new \DateTime()), 'date_accept' => ApiUtil::prepareDateTimeForDB($orderCreateInput->dateAccept) ?: null, 'date_handle' => ApiUtil::prepareDateTimeForDB($orderCreateInput->dateHandle) ?: null, 'date_delivered' => ApiUtil::prepareDateTimeForDB($orderCreateInput->dateDelivered) ?: null, 'date_due' => ApiUtil::prepareDateTimeForDB($orderCreateInput->dateDue) ?: null, 'status' => $orderCreateInput->status ?: 0, 'id_user' => $orderCreateInput->userId ?? null, // invoice 'invoice_email' => $orderCreateInput->invoiceAddress->email, 'invoice_name' => $orderCreateInput->invoiceAddress->name, 'invoice_surname' => $orderCreateInput->invoiceAddress->surname, 'invoice_phone' => $orderCreateInput->invoiceAddress->phone ?? '', 'invoice_firm' => $orderCreateInput->invoiceAddress->firm ?? '', 'invoice_ico' => $orderCreateInput->invoiceAddress->ico ?? '', 'invoice_dic' => $orderCreateInput->invoiceAddress->dic ?? '', 'invoice_street' => $orderCreateInput->invoiceAddress->street ?? '', 'invoice_city' => $orderCreateInput->invoiceAddress->city ?? '', 'invoice_zip' => $orderCreateInput->invoiceAddress->zip ?? '', 'invoice_custom_address' => $orderCreateInput->invoiceAddress->customAddress ?? '', 'invoice_state' => $orderCreateInput->invoiceAddress->state ?? '', 'invoice_country' => $orderCreateInput->invoiceAddress->country ?? '', // delivery 'delivery_name' => $orderCreateInput->deliveryAddress->name ?? $orderCreateInput->invoiceAddress->name ?? '', 'delivery_surname' => $orderCreateInput->deliveryAddress->surname ?? $orderCreateInput->invoiceAddress->surname ?? '', 'delivery_firm' => $orderCreateInput->deliveryAddress->firm ?? $orderCreateInput->invoiceAddress->firm ?? '', 'delivery_street' => $orderCreateInput->deliveryAddress->street ?? $orderCreateInput->invoiceAddress->street ?? '', 'delivery_city' => $orderCreateInput->deliveryAddress->city ?? $orderCreateInput->invoiceAddress->city ?? '', 'delivery_zip' => $orderCreateInput->deliveryAddress->zip ?? $orderCreateInput->invoiceAddress->zip ?? '', 'delivery_country' => $orderCreateInput->deliveryAddress->country ?? $orderCreateInput->invoiceAddress->country ?? '', 'delivery_state' => $orderCreateInput->deliveryAddress->state ?? $orderCreateInput->invoiceAddress->state ?? '', 'delivery_custom_address' => $orderCreateInput->deliveryAddress->customAddress ?? $orderCreateInput->invoiceAddress->customAddress ?? '', 'delivery_phone' => $orderCreateInput->deliveryAddress->phone ?? $orderCreateInput->invoiceAddress->phone ?? '', 'delivery_email' => $orderCreateInput->deliveryAddress->email ?? '', 'flags' => implode(',', $flags), 'note_user' => $orderCreateInput->noteUser ?? '', ]; $customData = []; foreach ($orderCreateInput->data ?? [] as $data) { $customData[$data->key] = $data->value; } $orderData['note_admin'] = json_encode($customData); if (findModule(\Modules::INVOICES) && $orderCreateInput->invoice && ObjectUtil::isPropertyInitialized($orderCreateInput->invoice, 'code')) { $orderData['invoice_no'] = $orderCreateInput->invoice->code ?? null; } if (findModule(\Modules::CURRENCIES)) { $orderData['id_language'] = $orderCreateInput->language ?? $languageContext->getDefaultId(); $orderData['currency'] = $orderCreateInput->currency ?? $currencyContext->getDefaultId(); $orderData['currency_rate'] = $orderCreateInput->currencyRate ?? $currencyContext->getOrDefault($orderData['currency'])->getRate(); } $purchaseState->setSource(OrderInfo::ORDER_SOURCE_API); if ($deliveryId = $orderCreateInput->deliveryType?->id ?? false) { $deliveryType = \DeliveryType::get($deliveryId, true); $purchaseState->setDeliveryType($deliveryType); if (($deliveryPrice = $orderCreateInput->deliveryType?->deliveryPrice) !== null) { $deliveryVat = $deliveryType->getPrice()->getVat()->asFloat(); $price = new Price(toDecimal($deliveryPrice)->removeVat($deliveryVat), $currencyContext->getOrDefault($orderData['currency']), $deliveryVat); $purchaseState->setDeliveryPrice($price); } } $purchaseState->setCustomData( [ 'order' => $orderData, ] ); $purchaseState = $this->purchaseUtil->recalculateTotalPrices($purchaseState); $order = $this->purchaseUtil->createOrderFromPurchaseState( purchaseState: $purchaseState, useDeliveryFromPurchaseState: true, onCreatedCallback: fn (int $orderId) => $this->insertOrderDropshipment($orderId, $orderCreateInput) ); $order->logHistory('[API] Objednávka vznikla přes API'); // log user note to order so it is visible in history if ($orderCreateInput->noteUser) { $order->logHistory($orderCreateInput->noteUser); } return new OrderMutateResponse(true, $this->getOrder($order->id, $fetchOptions)); }); }); } protected function validateOrderCreateInput(OrderCreateInput $input): void { if ($this->dropshipmentUtil) { if (!$this->dropshipmentUtil->getDropshipment($input->dropshipment->id)) { throw new GraphQLValidationException('OrderCreateInput.dropshipment.id: Given dropshipment ID does not exist!'); } if (empty($input->dropshipment->externalId)) { throw new GraphQLValidationException('OrderCreateInput.dropshipment.externalId: Order externalId cannot be empty!'); } $dropshipmentOrderExists = sqlQueryBuilder() ->select('id_order') ->from('order_dropshipment') ->where(Operator::equals(['id_external' => $input->dropshipment->externalId])) ->execute()->fetchOne(); if ($dropshipmentOrderExists) { throw new GraphQLValidationException( sprintf('OrderCreateInput.dropshipment.externalId: Order with this external ID already exists!') ); } } if ($input->status && !$this->checkOrderStatus($input->status)) { throw new GraphQLValidationException('Specified order status is not valid. Use configuration.orderStatuses to get available statuses.'); } if ($input->invoice) { if (ObjectUtil::isPropertyInitialized($input->invoice, 'code') && !empty($input->invoice->code) && $this->checkInvoiceExists($input->invoice->code)) { throw new GraphQLValidationException(sprintf('Invoice with this code "%s" already exists', $input->invoice->code)); } } if ($input->language && !$this->checkLanguageValid($input->language)) { throw new GraphQLValidationException(sprintf('Language "%s" is not supported.', $input->language)); } if ($input->currency && !$this->checkCurrencyValid($input->currency)) { throw new GraphQLValidationException(sprintf('Currency "%s" is not supported', $input->currency)); } if ($input->deliveryType?->id && !$this->checkDeliveryTypeValid($input->deliveryType->id)) { throw new GraphQLValidationException('Specified delivery type is not valid. Use configuration.deliveryTypes to get available delivery types.'); } if ($input->userId && !$this->checkUserExists($input->userId)) { throw new GraphQLValidationException(sprintf('User with this ID "%s" does not exist', $input->userId)); } } protected function insertOrderDropshipment(int $orderId, OrderCreateInput $input): void { $dropshipmentData = []; foreach ($input->dropshipment->data ?? [] as $data) { $dropshipmentData[$data->key] = $data->value; } $dropshipment = [ 'id_order' => $orderId, 'id_dropshipment' => $input->dropshipment->id, 'id_external' => $input->dropshipment->externalId, 'data' => json_encode($dropshipmentData), ]; sqlQueryBuilder() ->insert('order_dropshipment') ->directValues($dropshipment) ->execute(); } public function checkOrderStatus(int $orderStatus): bool { return in_array($orderStatus, array_keys(getOrderStatuses())); } public function checkInvoiceExists(?string $code): bool { return sqlQueryBuilder() ->select('id') ->from('orders') ->where(Operator::equals(['invoice_no' => $code])) ->execute()->fetchOne(); } public function checkLanguageValid(string $language): bool { return isset(Contexts::get(LanguageContext::class)->getAll()[$language]); } public function checkCurrencyValid(string $currency): bool { $currencyContext = Contexts::get(CurrencyContext::class); return $currencyContext->isValid($currency); } public function checkDeliveryTypeValid(int $deliveryId): bool { return \DeliveryType::get($deliveryId, true)->id > 0; } public function checkUserExists(int $userId): bool { return sqlQueryBuilder() ->select('id') ->from('users') ->where(Operator::equals(['id' => $userId])) ->execute()->fetchOne(); } }