loadXML())) { return; } $countryContext = Contexts::get(CountryContext::class); $countries = $countryContext->getAll(); $defaultCurrency = Contexts::get(CurrencyContext::class)->getDefaultId(); foreach ($xml->ORDER as $order) { if (!$this->isDropshipOrderValidToImport($order)) { continue; } $deliveryType = $this->getDeliveryType($order); $delivery_type = $order->DELIVERY_METHOD.' - '.$order->PAYMENT_TYPE; $id_delivery = null; if ($deliveryType) { $delivery_type = $deliveryType->name; $id_delivery = $deliveryType->id; } $deliveryPrice = $order->DELIVERY_PRICE + $order->COD_PRICE; $customer = $order->ADDRESS; $country = (string) $customer->COUNTRY; if (!($countries[$country] ?? false)) { $countries = array_keys($countries); $country = reset($countries); } $customer_name = explode(' ', (string) $customer->NAME); $name = array_shift($customer_name); $surname = implode(' ', $customer_name); $data = [ 'date_created' => (new \DateTime())->format('Y-m-d H:i:s'), 'status' => 0, 'id_delivery' => $id_delivery, 'delivery_type' => $delivery_type, 'flags' => 'DSM', 'note_invoice' => (string) $order->ID, 'note_admin' => json_encode([ 'mall' => [ 'order_id' => (string) $order->ID, 'cash_on_delivery' => (string) $order->COD, 'delivery_method_id' => (string) $order->DELIVERY_METHOD_ID, ], ]), 'source' => OrderInfo::ORDER_SOURCE_DROPSHIP, 'invoice_name' => $name ?? '', 'invoice_surname' => $surname ?? '', 'invoice_email' => (string) $customer->EMAIL, 'invoice_phone' => (string) $customer->PHONE, 'invoice_street' => (string) $customer->STREET, 'invoice_city' => (string) $customer->CITY, 'invoice_zip' => (string) $customer->ZIP, 'invoice_country' => $country, 'delivery_name' => $name ?? '', 'delivery_surname' => $surname ?? '', 'delivery_street' => (string) $customer->STREET, 'delivery_city' => (string) $customer->CITY, 'delivery_zip' => (string) $customer->ZIP, 'delivery_country' => $country, 'currency' => $defaultCurrency, ]; $orderObj = sqlGetConnection()->transactional(function () use ($order, $data, $deliveryPrice) { $orderObj = $this->createOrder($order, $data); // zaloguju dalsi informace o objednavce $orderObj->logHistory(implode('
', [ 'Číslo Mall objednávky: '.(string) $order->ID, 'ID výdejního místa: '.(string) $order->DELIVERY_METHOD_ID, 'SHIP_DATE: '.(string) $order->SHIP_DATE, ])); $orderID = $orderObj->id; // prepare order items $items = $this->prepareItems($order->ITEMS); // add delivery item $items[] = $this->getDeliveryItem($deliveryPrice); if ($order->DISCOUNT > 0) { // add discount item $items[] = $this->getDiscountItem(-1 * $order->DISCOUNT); } // insert order items foreach ($items as $item) { if ($item['id_product']) { $product = new \Product(); $product->createFromDB($item['id_product']); $product->sell($item['id_variation'], toDecimal($item['pieces'])->asInteger()); } $item['id_order'] = $orderID; $this->insertSQL('order_items', $item); $item['id'] = sqlInsertId(); $this->itemCreatedEvent( product: $product ?? null, idVariation: (int) $item['id_variation'], piecePrice: toDecimal($item['piece_price']), pieces: toDecimal($item['pieces'])->asInteger(), data: [ 'row' => $item, 'items_table' => 'order_items', ], order: $orderObj ); unset($product); } $orderObj->recalculate(round: false); return $orderObj; }); $this->modifyInsertedOrder($orderObj, $order); } } protected function getDeliveryItem($deliveryPrice): array { $deliveryPrice = toDecimal($deliveryPrice); $deliveryPrice = $deliveryPrice->removeVat(getVat()); return [ 'id_product' => null, 'id_variation' => null, 'pieces' => 1, 'pieces_reserved' => 1, 'piece_price' => $deliveryPrice, 'total_price' => $deliveryPrice, 'descr' => 'Doprava a platba', 'tax' => getVat(), 'note' => '{"item_type":"delivery"}', ]; } protected function getDiscountItem($discountPrice): array { $discountPrice = toDecimal($discountPrice); $discountPrice = $discountPrice->removeVat(getVat()); return [ 'id_product' => null, 'id_variation' => null, 'pieces' => 1, 'pieces_reserved' => 1, 'piece_price' => $discountPrice, 'total_price' => $discountPrice, 'descr' => 'Celková sleva', 'tax' => getVat(), 'note' => '{"item_type":"discount"}', ]; } protected function getExternalData(\SimpleXMLElement $xml): array { return [ (string) $xml->ID, [ 'cash_on_delivery' => (string) $xml->COD, 'delivery_method_id' => (string) $xml->DELIVERY_METHOD_ID, ], ]; } public function out(array $config): void { if (isDevelopment()) { return; } if (!($clientId = $config['api_key'] ?? null)) { $this->addActivityLog( 'V nastavení chybí API klíč', $config ); return; } $this->sendCancelledOrders($clientId); $this->sendShippedOrders($clientId); } protected function sendCancelledOrders($clientId) { $qb = sqlQueryBuilder()->select('o.id')->from('orders', 'o') ->where(Operator::isNotNull(JsonOperator::value('o.note_admin', 'mall.order_id'))) ->andWhere('o.status_storno = 1') ->andWhere(Operator::isNull(JsonOperator::value('o.note_admin', 'mall.cancelledSent'))) ->groupBy('o.id'); foreach ($qb->execute() as $item) { $order = new \Order(); $order->createFromDB($item['id']); $mallData = $order->getData('mall'); $params = [ 'confirmed' => true, 'status' => 'cancelled', ]; if ($this->sendOrderUpdate($clientId, $mallData['order_id'], $params)) { $mallData['cancelledSent'] = true; $order->setData('mall', $mallData); $order->logHistory('Objednávka v MALL byla aktualizována na status "cancelled"'); } } } protected function sendShippedOrders($clientId) { $qb = sqlQueryBuilder()->select('o.id')->from('orders', 'o') ->where(Operator::isNotNull(JsonOperator::value('o.note_admin', 'mall.order_id'))) ->andWhere('o.status_storno = 0 AND o.package_id IS NOT NULL') ->andWhere(Operator::inIntArray($this->getShippedStatuses(), 'o.status')) ->andWhere(Operator::isNull(JsonOperator::value('o.note_admin', 'mall.shippedSent'))) ->groupBy('o.id'); foreach ($qb->execute() as $item) { $order = new \Order(); $order->createFromDB($item['id']); $mallData = $order->getData('mall'); $packages = $this->orderInfo->getPackages($order); $package = $packages[$order->package_id] ?? end($packages); if (!$package) { continue; } $params = [ 'confirmed' => true, 'status' => 'shipped', 'tracking_number' => $package['package_id'], 'tracking_url' => $package['track_url'] ?? '', ]; if ($this->sendOrderUpdate($clientId, $mallData['order_id'], $params)) { $mallData['shippedSent'] = true; $order->setData('mall', $mallData); $order->logHistory('Objednávka v MALL byla aktualizována na status "shipped"'); } } } protected function getShippedStatuses(): ?array { return getStatuses('handled'); } protected function sendOrderUpdate(string $clientId, $mall_order_id, array $params): bool { $ch = curl_init(); $url = self::URL_API.$mall_order_id.'?client_id='.$clientId; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type:application/json']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params)); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); $result = curl_exec($ch); curl_close($ch); $result = json_decode($result, true); if ('OK' != $result['result']['status'] ?? null) { $message = $result['result']['message'] ?? ''; $data = [ 'mall_order_id' => $mall_order_id, 'params' => $params, 'result' => $result['result'] ?? null, ]; addActivityLog(ActivityLog::SEVERITY_ERROR, ActivityLog::TYPE_SYNC, sprintf('Dropshipment MALL: chyba při odeslání aktualizace stavů objednávky "%s"', $message), $data); if (str_starts_with($message, 'Final status') && str_ends_with($message, 'cannot be changed')) { return true; } return false; } return true; } protected function isDropshipOrderValidToImport(\SimpleXMLElement $xml): bool { return parent::isDropshipOrderValidToImport($xml) && $this->isValidToImport($xml); } protected function isValidToImport(\SimpleXMLElement $order): bool { $found = sqlQueryBuilder() ->select('id') ->from('orders') ->where( Operator::equals( [ JsonOperator::value('note_admin', 'mall.order_id') => (string) $order->ID, ] ) )->execute()->fetchColumn(); // objednavka uz je vytvorena if ($found) { return false; } return true; } protected function createOrder(\SimpleXMLElement $order, array $data): \Order { return $this->createDropshipOrder($order, $data); } protected function getDeliveryType(\SimpleXMLElement $orderItem): ?\DeliveryType { return $this->getDeliveryTypeByConfiguration($orderItem); } private function prepareItems(\SimpleXMLElement $items): array { $result = []; foreach ($items as $item) { $itemCode = (string) $item->ID; $parsed = explode('_', $itemCode); $productID = $parsed[0] ?? $itemCode; $variationID = $parsed[1] ?? null; // check that productID exists if (!$this->selectSQL('products', ['id' => $productID], ['id'])->fetch()) { $productID = null; } // check that variationID exists if ($variationID && !$this->selectSQL('products_variations', ['id' => $variationID], ['id'])->fetch()) { $variationID = null; } // create name of item $descr = 'Položka kód: '.$itemCode; if ($productID) { $title = $this->selectSQL('products', ['id' => $productID], ['title'])->fetchColumn(); if (!empty($title)) { $descr = $title; } if ($variationID) { $title = $this->selectSQL('products_variations', ['id' => $variationID], ['title'])->fetchColumn(); if (!empty($title)) { $descr .= ' ('.$title.')'; } } } $price = toDecimal((string) $item->PRICE); $vat = (string) $item->VAT; $pieces = toDecimal((string) $item->QUANTITY); $price = $price->removeVat($vat); $itemTotalPrice = $price->mul($pieces); $result[] = $this->modifyItem([ 'id_product' => $productID, 'id_variation' => $variationID, 'pieces' => $pieces, 'pieces_reserved' => (string) $item->QUANTITY, 'piece_price' => $price, 'total_price' => $itemTotalPrice, 'tax' => (string) $item->VAT, 'descr' => $descr, 'note' => json_encode(['item_type' => OrderItemInfo::TYPE_PRODUCT]), ], $item); } return $result; } protected function getDeliveryTypeByConfiguration(\SimpleXMLElement $order): ?\DeliveryType { $deliveryMethod = (string) $order->DELIVERY_METHOD; $country = (string) $order->ADDRESS->COUNTRY; $cod = (string) $order->COD; $config = $this->getConfiguration(); $deliveryId = null; foreach ($config['deliveries'] ?? [] as $item) { if ($deliveryMethod == $item['id_external'] && (empty($item['country']) || $item['country'] == $country)) { $deliveryId = (int) $item['id_delivery']; break; } } if ($cod > 0) { $paymentId = empty($config['payments']['cod']) ? null : (int) $config['payments']['cod']; } else { $paymentId = empty($config['payments']['paid']) ? null : (int) $config['payments']['paid']; } return $this->findDeliveryType($deliveryId, $paymentId); } public function prepareConfigurationData(array $data): array { foreach ($data['deliveries'] ?? [] as $key => $item) { $item = array_filter($item); if (!empty($item['delete'])) { unset($data['deliveries'][$key]); continue; } if ($key <= 0) { if (!empty($item['id_external']) && !empty($item['id_delivery'])) { $data['deliveries'][] = $item; } unset($data['deliveries'][$key]); } } return $data; } }