sellerUtil = $sellerUtil; } public function process(): void { $this->processOrdersToDRS(); $this->processOrdersFromDRS(); } public function processOrdersToDRS(): void { if (isDevelopment()) { return; } $forceSendSpec = 'JSON_VALUE(o.note_admin, "$.sendToDRS") = 1'; $qb = sqlQueryBuilder() ->select('o.id') ->from('orders', 'o') ->leftJoin('o', 'drs_orders', 'do', 'o.id = do.id_order') // neni zapsana v DRSu ->andWhere('do.id_drs IS NULL') // nestornovane objednavky ->andWhere( Operator::orX( Operator::equals(['o.status_storno' => 0]), $forceSendSpec ) ) // neni vyrizena ->andWhere( Operator::not( Operator::inIntArray(getStatuses('handled'), 'o.status') ) ) // objednavka nema flag NP - Nakup na prodejne, nebo O - Historicka objednavka ->andWhere( Operator::not( Operator::findInSet(['NP', 'O'], 'o.flags', 'OR') ) ) // Do DRSu zapisujeme ihned pouze ciste rezervace // Nebo zapiseme objednavky, ktere maji nastaveny v datech sendToDRS = 1 - to muze nastavit DataGo synchronizace, aby se // objednavka zapsala do DRSu ->andWhere( Operator::orX( $this->getOrderTypeSpec([OrderType::ORDER_RESERVATION]), $forceSendSpec ) ); foreach ($qb->execute() as $item) { $order = \Order::get((int) $item['id']); if (($order->getFlags()['NP'] ?? false) || ($order->getFlags()['O'] ?? false)) { continue; } // kolikaty pokus zalozeni objednavky if (!($insertTry = $order->getData('drsInsertTry'))) { $insertTry = 0; } $insertTry++; // nastavim $result na null, aby se mi do ActivityLogu nezapsal $result z predesle objednavky $result = null; try { $operation = 'BookDocument'; $result = $this->drsApi->createOrder( $this->getOrderData($order), $operation ); $documentNumber = $result['DocumentBookResult']['Result']['@attributes']['DocumentNumber'] ?? null; sqlQueryBuilder() ->insert('drs_orders') ->directValues( [ 'id_drs' => $documentNumber, 'id_order' => $order->id, 'data' => json_encode(['response' => $result]), ] )->execute(); $this->updateOrderStatusAfterDRSInsert($order, $documentNumber); } catch (\Throwable $e) { // objednavka je v DRSu uz zapsana - DRS nam mohl vratit nejakou chybu, ale objednavku potom zapsa, takze si potrebuju doplnit mapovani if ($e instanceof DRSException && strpos($e->getMessage(), 'duplicate key') !== false) { sqlQueryBuilder() ->insert('drs_orders') ->directValues( [ 'id_drs' => $order->order_no, 'id_order' => $order->id, 'data' => [], ] )->execute(); $this->updateOrderStatusAfterDRSInsert($order, $order->order_no); continue; } // aktualizuju pocet pokusu o zalozeni objednavky $order->setData('drsInsertTry', $insertTry); // logovat zacinam az kdyz se to nepovede po vice jak 2 pokusech, protoze se muze stat, ze se to na poprve // zapise, ale kvuli DRSu, kterej nevrati info o tom, ze to zapsal, si nezapiseme mapovani a v dalsim // runu to zkusime zapsat znova, kde zjistime, ze tam objednavka uz je a vytvorime mapovani, ale v ActivityLogu // pak zbytecne otravuje chybo hlaska o nezapsani objednavky if ($insertTry > 2) { $this->logger->logException( $e, sprintf('[DRS] Objednávku %s se nepodařilo zapsat', $order->order_no), [ 'message' => $e->getMessage(), 'result' => $result ?? [], ] ); } } } } public function processOrdersFromDRS(): void { $qb = sqlQueryBuilder() ->select('o.id, o.order_no, do.id_drs') ->from('orders', 'o') ->join('o', 'drs_orders', 'do', 'do.id_order = o.id') ->andWhere(Operator::equals(['o.status_storno' => 0])) ->andWhere('JSON_VALUE(COALESCE(do.data, "{}"), "$.completed") IS NULL') // objednavka nema flag NP - Nakup na prodejne, nebo O - Historicka objednavka ->andWhere( Operator::orX( Operator::not( Operator::findInSet(['NP', 'O'], 'o.flags', 'OR') ), // TODO: tohle by se mohlo dat casem pryc // Aktualizace stavu objednavek ze stareho e-shopu - aktualizuje to pouze dva mesice zpatky od spusteni // pompa u nas Operator::andX( // beru pouze objednavky s flagem O Operator::findInSet(['O'], 'o.flags'), // objednavka nesmi byt ukoncena Operator::not( Operator::equals(['o.status' => $this->configuration->getOrderFinalStatus()]) ), // dva mesice zpet od spusteni eshopu u nas 'TIMESTAMPDIFF(MONTH, date_created, "2022-10-05 00:00:00") <= 2' ) ) ); $orders = []; foreach ($qb->execute() as $item) { $orders[(int) $item['id']] = $item['order_no']; } // 10 Přijatá // 50 Expedovaná // 60 Připravená k osobnímu odběru // 70 Hotová a ukončená // 80 Zrušená // 90 Převoz // 91 Nekompletní foreach (array_chunk(array_values($orders), 1000) as $chunk) { foreach ($this->drsApi->getOrdersInfo($chunk) as $item) { if (!($id = array_flip($orders)[$item['number']] ?? null)) { continue; } $order = \Order::get((int) $id); // prislo cislo baliku if (!empty($item['packageNumber'])) { $this->setOrderPackageNumber($order, $item['packageNumber']); } switch ($item['status']) { // Objednavka se zpracovava case '40': $this->changeOrderStatus($order, 2, OrderInProgressEmail::getType()); break; // Prevoz case '90': $this->changeOrderStatus($order, 3); break; // Objednavka pripravena k osobnimu vyzvednuti case '60': $this->changeOrderStatus($order, 5, OrderInPersonReadyEmail::getType()); break; // Nekompletní - objednavka je pripravena k osobnimu prevzeti, ale neni kompletni, protoze nejake zbozi chybi case '91': // docasne vypnuto // $this->updateOrderItemsFromDRS($order); // pokud uz je ve finalnim stavu, tak ji neprepinam zpatky to nekompletniho stavu if ($order->status != $this->configuration->getOrderFinalStatus()) { $this->changeOrderStatus($order, 7, OrderNotCompleteEmail::getType()); } break; // Expedovaná case '50': $email = OrderSentEmail::getType(); if ($this->isOrderInPerson($order)) { $email = null; } $this->changeOrderStatus($order, 4, $email); break; // Hotová a ukončená case '70': $order->logHistory('[DRS] Objednávka kompletní - Hotová a ukončená'); $this->changeOrderStatus($order, $this->configuration->getOrderFinalStatus()); $this->setOrderCompleted($order); break; // Objednavka zrusena case '80': $this->setOrderCompleted($order); $order->storno(false); break; } } } } public function getOrderData(\Order $order): array { $branchId = $this->getOrderDestinationBranchId($order); $data = [ 'login' => [ '@attributes' => [ 'LoggedUser' => '', 'user' => '0', 'passwd' => '', 'branch' => $this->getLoginBranch($order), 'CreationBranch' => $this->getCreationBranchId(), ], 'command' => [ '@attributes' => [ 'action' => 'create', ], 'document' => [ '@attributes' => $this->getDocumentAttributes($order), 'text' => [ '@value' => $this->getOrderNumber($order), '@attributes' => [ 'number' => 1, ], ], 'item' => [], 'memo' => [ $this->getXMLMemoItem('Email', $order->invoice_email), $this->getXMLMemoItem('Order_Branch', $branchId), $this->getXMLMemoItem('Order_Paid', $order->isPaid() ? 'true' : 'false'), $this->getXMLMemoItem('NameDelivery', $this->getDeliveryName($order)), $this->getXMLMemoItem('NamePayment', $this->getPaymentName($order)), $this->getXMLMemoItem('Puvod_zbozi', $this->getOriginOfGoods($order)), $this->getXMLMemoItem('Poznamka', $this->getDRSOrderNote($order)), $this->getXMLMemoItem('WayOfPayment', $this->getDeliveryTypeCode($order)), $this->getXMLMemoItem('WayOfTransport', $this->getDeliveryTypeCode($order)), // Natvrdo tady posilam CZK, protoze maji v DRSu nastaveny automaticky procesy, ktery s jinyma hodnotama asi nepocataji // a zacalo to kvuli tomu delat nejaky problemy v DRSu $this->getXMLMemoItem('CurrencyForeignISOCode', 'CZK'), $this->getXMLMemoItem('CurrencyHomeISOCode', 'CZK'), $this->getXMLMemoItem('CurrencyRate', '1'), // $this->getXMLMemoItem('Status_WEB_objednavky', 'Přijatá', '10'), $this->getXMLMemoItem('ContainsCorrectPrices', 'True'), $this->getXMLMemoItem('InvoiceName', implode(' ', array_filter([$order->invoice_name, $order->invoice_surname]))), $this->getXMLMemoItem('InvoiceCompany', $order->invoice_firm), $this->getXMLMemoItem('InvoiceStreet', $order->invoice_street), $this->getXMLMemoItem('InvoiceCity', $order->invoice_city), $this->getXMLMemoItem('InvoiceZIP', $order->invoice_zip), $this->getXMLMemoItem('InvoiceCountryCode', $order->invoice_country), $this->getXMLMemoItem('InvoiceTIN', ''), $this->getXMLMemoItem('InvoiceIN', ''), $this->getXMLMemoItem('PhoneNumber', $order->invoice_phone), $this->getXMLMemoItem('DeliveryName', implode(' ', array_filter([$order->delivery_name, $order->delivery_surname]))), $this->getXMLMemoItem('DeliveryCompany', $order->delivery_firm), $this->getXMLMemoItem('DeliveryStreet', $order->delivery_street), $this->getXMLMemoItem('DeliveryCity', $order->delivery_city), $this->getXMLMemoItem('DeliveryZIP', $order->delivery_zip), $this->getXMLMemoItem('DeliveryCountryCode', $order->delivery_country), ], 'Addresses' => [ 'Address' => [ '@attributes' => [ 'TypeRecUID' => '00000000-0000-0000-0000-000000000000', 'Name' => implode(' ', array_filter([$order->invoice_name, $order->invoice_surname])), 'Street' => $order->invoice_street, 'Country' => $order->invoice_country, 'HouseNumber' => '', 'City' => $order->invoice_city, 'Zip' => $order->invoice_zip, 'MatchCode' => '', 'AccountNr' => '', 'Note1' => '', 'Note2' => '', 'Note3' => '', ], ], ], ], ], ], ]; // Pokud je to objednavka se zavozem na prodejnu, tak musim plnit datum zavozu if ($this->orderingUtil->getOrderType($order) === OrderType::ORDER_TRANSPORT_RESERVATION) { // deliveryDate mam ulozeny v datech delivery_data na objednavce $deliveryData = $order->getData('delivery_data'); if ($deliveryData['deliveryDate'] ?? false) { $data['login']['command']['document']['memo'][] = $this->getXMLMemoItem( 'Datum_zavozu', $deliveryData['deliveryDate'], $deliveryData['deliveryDate'] ); } } // Pokud ma objednavka nastaveno sendToDRS - znamena to, ze je ve stavu prevoz nebo zrusena z DataGo, takze stav rovnou nastavim if ($order->getData('sendToDRS')) { if ($order->status_storno == 1) { $data['login']['command']['document']['memo'][] = $this->getXMLMemoItem('Status_WEB_objednavky', 'Zrušená', '80'); } else { $data['login']['command']['document']['memo'][] = $this->getXMLMemoItem('Status_WEB_objednavky', 'Převoz', '90'); } } $deliveryItem = null; $index = 1; // Pridam polozky objednavky foreach ($order->fetchItems() as $orderItem) { $isChargeItem = !empty($orderItem->getNote()['charge']); if (!$isChargeItem && $this->orderItemInfo->getItemType($orderItem) === OrderItemInfo::TYPE_DELIVERY) { $deliveryItem = $orderItem; continue; } $data['login']['command']['document']['item'][] = $this->getXMLItem($order, $orderItem, $index++); } // Pridam dopravu a platbu do polozek foreach ($this->getDeliveryAndPaymentItem($order, $deliveryItem) as $type => $item) { $data['login']['command']['document']['item'][] = $this->getXMLDeliveryItem($order, $item, $type, $index++); } return $data; } protected function processItem(array $item): void { // TODO: Implement processItem() method. } protected function getItems(): iterable { return []; } public function setOrderCompleted(\Order $order): void { // mark as completed sqlQueryBuilder() ->update('drs_orders') ->set('data', 'JSON_SET(COALESCE(data, "{}"), "$.completed", 1)') ->where( Operator::equals( [ 'id_order' => $order->id, ] ) )->execute(); } public function getLoginBranch(\Order $order): string { if ($this->configuration->isNejhracka()) { if ($order->getLanguage() === 'sk') { return '404'; } return '403'; } if ($order->getLanguage() === 'sk') { return '402'; } return '401'; } protected function updateOrderItemsFromDRS(\Order $order): void { $diffResult = $this->pompoOrderUtil->getOrderItemDifference($order); if (!$diffResult['hasDiff']) { return; } $this->pompoOrderUtil->updateOrderByDifference( $order, $diffResult ); } private function updateOrderStatusAfterDRSInsert(\Order $order, string $documentNumber): void { if ($order->status > 1) { // odesilam ji do DRSu pozdeji - uz neni ve stavu nova, ale treba ve stavu prevoz $order->logHistory( sprintf('[DRS] Objednávka byla úspěšně nahrána do DRSu: %s', $documentNumber) ); } else { // odesilam do DRSu novou objednavku $order->changeStatus( 1, sprintf('[DRS] Objednávka byla úspěšně nahrána do DRSu: %s', $documentNumber), false ); } } private function getDRSOrderNote(\Order $order): string { $note = []; if ($partId = $order->getData('orderPartId')) { $note[] = 'Část '.$partId; } $note[] = $this->getOrderNote($order); return implode('; ', array_filter($note)); } private function getDeliveryAndPaymentItem(\Order $order, ?OrderItem $deliveryItem): array { $result = $this->splitDeliveryItem($order, $deliveryItem); if ($result['delivery'] ?? false) { $result['delivery']['code'] = $this->isOrderInPerson($order) ? '995003' : '995001'; $result['delivery']['ean'] = $this->isOrderInPerson($order) ? '2500000251117' : '2500000251115'; $result['delivery']['descr'] = $this->isOrderInPerson($order) ? 'Manipulační poplatek' : 'Poštovné a balné'; } if ($result['payment'] ?? false) { $result['payment']['code'] = '995002'; $result['payment']['ean'] = '2500000251116'; $result['payment']['descr'] = 'Doběrečné'; } return $result; } private function getOrderDestinationBranchId(\Order $order): string { // $branchId = $this->getOrderDefaultBranchId($order); $branchId = ''; // select branchId by selected seller if ($seller = $this->orderingUtil->getOrderSeller($order)) { if (!empty($seller['data']['branchId'])) { $branchId = (string) $seller['data']['branchId']; } } // for testing use branchId 9998 if (isDevelopment()) { $branchId = '9998'; } return $branchId; } private function getOriginOfGoods(\Order $order): string { if ($this->orderingUtil->getOrderType($order) === OrderType::ORDER_RESERVATION) { return 'Prodejna'; } return 'Centrala'; } private function getDeliveryTypeCode(\Order $order): string { if ($deliveryType = $order->getDeliveryType()) { return $deliveryType->getCustomData()['drs_code'] ?? ''; } return ''; } private function getDeliveryName(\Order $order): string { if ($deliveryType = $order->getDeliveryType()) { if ($delivery = $deliveryType->getDelivery()) { return $delivery->name; } } return ''; } private function getPaymentName(\Order $order): string { if ($deliveryType = $order->getDeliveryType()) { if ($payment = $deliveryType->getPayment()) { return $payment->getName(); } } return ''; } private function getCreationBranchId(): string { if ($this->configuration->isNejhracka()) { return '1673'; } return '1363'; } private function getOrderDefaultBranchId(\Order $order): string { if ($this->configuration->isNejhracka()) { // nejhracka - SK if ($order->getLanguage() === 'sk') { return '404'; } // nejhracka - CZ return '403'; } // pompo - SK if ($order->getLanguage() === 'sk') { return '402'; } // pompo - CZ return '401'; } private function getXMLItem(\Order $order, OrderItem $item, int $index): array { $code = $item->getCode(); $ean = $item->getEAN(); $descr = $item->getDescr(); if ($this->isOrderItemTransportCharge($item)) { $code = '995003'; $ean = '2500000251117'; $descr = ''; } if ($chargeCode = $this->pompoOrderUtil->getOrderItemChargeCode($item)) { $code = $chargeCode; } $attributes = [ [ '@attributes' => [ 'Name' => 'ItemID', 'Value' => $item->getId(), ], ], ]; $itemInfo = $order->getItemInfo($item); // pridam informaci o variante do poznamky polozky if ($itemInfo['selectedVariation'] ?? false) { $attributes[] = [ '@attributes' => [ 'Name' => 'Poznamka_polozka', 'Value' => $itemInfo['selectedVariation'], ], ]; } return [ '@attributes' => [ 'ItemNumber' => $index, 'RecUID' => Uuid::v4()->toRfc4122(), 'CreationTime' => $order->date_created->format('m/d/Y H:i:s'), 'ProducerColor' => $item->getId(), 'article' => $code, 'ean_code' => $ean, 'MainPrice' => 'RetailPrice', 'RetailPrice' => $item->getPiecePrice()->getPriceWithVat()->asFloat(), 'PriceEntered' => $item->getPiecePrice()->getPriceWithVat()->asFloat(), 'main_price' => $item->getPiecePrice()->getPriceWithVat()->asFloat(), 'vat_percent' => $item->getVat(), 'vat_value' => $item->getPiecePrice()->getVatValue()->asFloat(), 'amount' => $item->getPieces(), 'Description' => $descr, ], 'Attribute' => $attributes, ]; } private function getXMLDeliveryItem(\Order $order, array $item, string $type, int $index): array { return [ '@attributes' => [ 'ItemNumber' => $index, 'RecUID' => Uuid::v4()->toRfc4122(), 'CreationTime' => $order->date_created->format('m/d/Y H:i:s'), 'article' => $item['code'], 'ean_code' => $item['ean'], 'MainPrice' => 'RetailPrice', 'RetailPrice' => $item['price'], 'PriceEntered' => $item['price'], 'main_price' => $item['price'], 'vat_percent' => $item['vat'], 'amount' => $item['quantity'], 'Description' => $item['descr'], ], 'Attribute' => [ [ '@attributes' => [ 'Name' => 'ItemID', 'Value' => $item['id'], ], ], ], ]; } private function getDocumentAttributes(\Order $order): array { $customerId = '0'; if ($order->id_user) { $drsId = sqlQueryBuilder() ->select('id_drs') ->from('drs_users') ->where(Operator::equals(['id_user' => $order->id_user])) ->execute()->fetchOne(); if ($drsId) { $customerId = $drsId; } } // GUID objednavky - kazdy objednavce ho vygeneruju jen pri prvnim zapisu, protoze se stalo, ze DRS nevratil odpoved, ale // objednavku zapsal, ale e-shop se ji pak pokusil zapsat znovu... a DRS vratil odpoved treba az na po treti, takze // objednavka byla v DRSu zapsana 3x if (!($guid = $order->getData('drsGuid'))) { $guid = Uuid::v4()->toRfc4122(); $order->setData('drsGuid', $guid); } return [ 'GUID' => $guid, 'address' => $customerId, 'DocumentCreator' => 'eshop', 'date' => $order->date_created->format('d.m.Y'), 'time' => $order->date_created->format('H:i:s'), 'branch_from' => $this->getOrderDefaultBranchId($order), 'branch_to' => '0', 'POSFlowStockEffect' => '-1', 'POSFlowSubType' => '106', 'type' => '27', 'book' => 'True', 'AbonentFirstName' => '', 'AbonentSurName' => '', 'AbonentNumber' => '0', 'FinancialEffect' => '-1', 'FinancialEffectCaption' => '', 'LinkedDocRecUID' => '00000000-0000-0000-0000-000000000000', 'AutoprocessNumber' => '-1', // Natvrdo tady posilam CZK, protoze maji v DRSu nastaveny automaticky procesy, ktery s jinyma hodnotama asi nepocataji // a zacalo to kvuli tomu delat nejaky problemy v DRSu 'CurrencyForeignISOCode' => 'CZK', // $order->getCurrency(), 'CurrencyHomeISOCode' => 'CZK', // $order->getCurrency(), 'CurrencyRate' => '1', // $order->currency_rate, 'PriceWithVAT' => 'TRUE', 'HasFixedDocumentNumber' => 'False', 'TakoverWorkflowID' => '', 'TotalPrice' => $order->getTotalPrice()->getPriceWithVat()->asFloat(), ]; } private function getAttributes(array $attributes): array { $result = []; foreach ($attributes as $key => $value) { $result[$key] = $value; } return $result; } private function getXMLMemoItem(string $key, string $value, ?string $shortText = null): array { return [ '@attributes' => [ 'key' => $key, 'Text' => $shortText ?: $value, 'LongText' => $value, ], ]; } }