returnsUtil = $returnsUtil; } public function process(array $data): void { $this->log($data); if (empty($data['OrderId'])) { return; } // jedna se o vratku, takze zpracovavam jinak if ($sapReturnId = $this->sapUtil->getAdditionalFieldValue($data['Additionals'] ?? [], 'SAP_RETURN_ID')) { $this->returnSynchronizer->processReturn($sapReturnId, $data); return; } try { $order = \Order::createFromDbOrderNo($data['OrderId']); } catch (\InvalidArgumentException $e) { return; } if ($this->checkOrderStorno($order, $data)) { return; } // nacteni novych stavu a mailu, ktery ma poslat [$newStatus, $orderMessage, $isStorno] = $this->getOrderNewStatus($order, $data); // tohle delam kvuli tomu, aby vsechny logHistory byly zalogovany uz pod tim nastavajicim stavem viz. tiket #16162 $previousStatus = $order->status; $order->status = $newStatus; try { // update order by SAP $this->sapOrderUpdater->updateOrderBySAP($order); } catch (\Throwable $e) { if (isLocalDevelopment()) { throw $e; } if ($e instanceof SAPException) { $order->logHistory('[SAP] Při aktualizaci objednávky podle SAPu došlo k chybě: '.$e->getMessage()); } else { $this->sentryLogger->captureException($e); } } // ulozit package number - deprecated, package number by se mel ukladat uz v ramci volani `updateOrderBySAP` $this->updateOrderTracking($order, $data); // ulozit si invoice number if ($invoiceNumber = $this->getInvoiceNumber($data['Additionals'] ?? [])) { // pole s cislama faktur, protoze se muze stat, ze jich v SAPu bylo vytvoreno vic if (!($invoiceNumbers = $order->getData('sapInvoiceNumbers'))) { $invoiceNumbers = []; } // pokud faktura jeste nebyla odeslana, tak ji posleme if (!in_array($invoiceNumber, $invoiceNumbers)) { $invoiceNumbers[] = $invoiceNumber; // ulozim si cislo faktury $order->setData('sapInvoiceNumber', $invoiceNumber); // aktualizuju seznam s cislama faktur $order->setData('sapInvoiceNumbers', array_unique($invoiceNumbers)); // zalogovat si číslo faktury $order->logHistory(sprintf('[SAP] Číslo faktury: %s', $invoiceNumber)); // prerazim odkaz na fakturu odkazem, kde je specifikovane i cislo faktury $order->setPlaceholder('ODKAZ_FAKTURA', path( 'kupshop_orderingbundle_pdfinvoice', [ 'id_order' => $order->id, 'cf' => $order->getSecurityCode(), 'invoiceNumber' => $invoiceNumber, ], Router::ABSOLUTE_URL ) ); if (!$order->getData('sapDateInvoice')) { $order->setData('sapDateInvoice', time()); } if (!$this->isDropshipmentOrder($order)) { // odeslat email s fakturou $order->sendEmail(null, 'faktura'); } } } if ($newStatus !== null && $newStatus != $previousStatus) { $order->logHistory( sprintf('[SAP] Změna stavu %s: %s', $order->source === OrderInfo::ORDER_SOURCE_RESERVATION ? 'rezervace' : 'objednávky', $data['OrderState']) ); // hack aby prosla zmena stavu $order->status = $previousStatus; $order->changeStatus($newStatus, null, $orderMessage === null ? null : true, $orderMessage); $this->updateStatusCustomDate($order, $newStatus); } if ($isStorno) { $order->storno(false, '[SAP] Stornování objednávky', false); } } public function processToSAP(): void { $this->processOrdersToSAP(); $this->processUpdatePaidOrdersToSAP(); } protected function getHandledFields(): array { return []; } private function processUpdatePaidOrdersToSAP(): void { if (isLocalDevelopment()) { return; } $qb = sqlQueryBuilder() ->select('o.id, o.order_no, so.id_sap') ->from('orders', 'o') ->join('o', 'sap_orders', 'so', 'so.id_order = o.id') ->andWhere( Order::byPaymentMethods([\Payment::METHOD_TRANSFER, \Payment::METHOD_ONLINE]) ) ->andWhere(Operator::equals(['o.status_payed' => 1])) ->andWhere('JSON_VALUE(so.data, "$.paidSent") IS NULL'); foreach ($qb->execute() as $item) { try { $order = \Order::get((int) $item['id']); } catch (\InvalidArgumentException $e) { $this->sentryLogger->captureException($e); continue; } if ($this->api->updateOrderPayment($order)) { $order->logHistory('[SAP] Informace o zaplacení byla odeslána'); } // po zaplaceni jsou vygenerovany kupony, ktere musime v SAPu zaktivovat if ($coupons = $this->sapUtil->getOrderCreatedCoupon($order)) { try { $this->api->activateCoupons($item['id_sap'], $coupons, true); $order->logHistory('[SAP] Byly zaktivovány kupóny: '.implode(', ', $coupons).'; CausingDocument: '.$item['id_sap']); } catch (SAPException $e) { $order->logHistory('[SAP] Nepodařilo se aktivovat kupóny: '.$e->getMessage()); } } } } private function processOrdersToSAP(): void { if (isLocalDevelopment()) { return; } $qb = sqlQueryBuilder() ->select('o.id') ->from('orders', 'o') ->leftJoin('o', 'sap_orders', 'so', 'so.id_order = o.id') // objednavka neni stornovana ->andWhere(Operator::equals(['o.status_storno' => 0])) // a neni vyrizena ->andWhere( Operator::not( Operator::inIntArray(getStatuses('handled'), 'o.status') ) ) // objednavka nesmi byt ve stavu "chyba" - pokud je ve stavu chyba, tak ji uz nezkousim odeslat do SAPu ->andWhere( Operator::not( Operator::equals(['o.status' => $this->configuration->getOrderErrorStatus()]) ) ) // objednavka neni v SAPu ->andWhere('so.id_sap IS NULL') // objednavka nesmi mit flag "OLD" ->andWhere( Operator::not( Operator::findInSet(['OLD'], 'o.flags') ) ) ->groupBy('o.id'); if (findModule(\Modules::DROPSHIP)) { $dropshipOrdersDisabled = $this->configuration->getOrderConfig()['disable_dropshipment_orders'] ?? 'N'; // vypnuli odesilani drosphipment objednavek do SAPu if ($dropshipOrdersDisabled === 'Y') { $qb->leftJoin('o', 'order_dropshipment', 'od', 'od.id_order = o.id') ->andWhere('od.id_external IS NULL'); } } foreach ($qb->sendToMaster()->execute() as $item) { $order = new \Order(); $order->createFromDB($item['id']); // pocet pokusu, kolikrat se objednavka zkusila zapsat do SAPu if (!($try = $order->getData('sapCreateTry'))) { $try = 1; } try { if ($sapOrderId = $this->api->createOrder($order, true)) { $order->logHistory( sprintf('[SAP] Objednávka byla zapsána do SAPu; SAPID: %s', $sapOrderId) ); } } catch (SAPException $e) { if ($e instanceof SAPManyException) { // Duplicita pro zakazku if ($e->hasExceptionWithCode(['007'])) { // vytahnu si správný proces a základě jazyku objednávky $process = $this->sapOrderUtil->getOrderProcess($order); // zkusim si fetchnout objednavku z sapu podle kodu objednavky a to ID kdyztak doplnim if ($sapOrder = $this->api->getClient()->orderInfo($order->order_no, $process)) { if (!empty($sapOrder->SapOrderId)) { $this->sapUtil->createMapping( MappingType::ORDERS, $sapOrder->SapOrderId, (int) $order->id ); $order->logHistory( sprintf('[SAP] Objednávka byla zapsána do SAPu; SAPID: %s', $sapOrder->SapOrderId) ); continue; } } // pokud proste nenajdu objednavku a sap rika, ze tam uz existuje, tak taky udelam, ze neexistuje $order->logHistory( '[SAP] Objednávku se nepodařilo zapsat, protože v SAPu už existuje' ); // vytvorim mapping, abych to uz nezkousel znovu sqlGetConnection()->transactional(function () use ($order) { $this->sapUtil->createMapping( MappingType::ORDERS, 'DUP_'.$order->id, (int) $order->id ); $this->sapUtil->setMappingData( MappingType::ORDERS, (int) $order->id, ['paidSent' => true] ); }); continue; } } $this->activityLog->addActivityLog( ActivityLog::SEVERITY_ERROR, ActivityLog::TYPE_SYNC, sprintf('Objednávku "%s" se nepodařilo zapsat do SAP, pokus: %s! Chyba: %s', $order->order_no, $try, $e->getMessage()), [ 'try' => $try, 'message' => $e->getMessage(), ] ); $order->setData('sapCreateTry', ++$try); if ($try > 4) { $order->changeStatus( $this->configuration->getOrderErrorStatus(), sprintf('[SAP] Objednávku se nepodařilo zapsat do SAPu. Chyba: %s', $e->getMessage()), false ); } } } } private function getPackageNumber(array $data): ?string { return $this->sapUtil->getAdditionalFieldValue($data, 'PACKAGENR'); } private function getInvoiceNumber(array $data): ?string { return $this->sapUtil->getAdditionalFieldValue($data, 'INVOICE_NR'); } /** * Vratí čislo dobropisu. */ private function getCreditNoteNumber(array $data): ?string { return $this->sapUtil->getAdditionalFieldValue($data, 'CRMEMO_NR'); } private function checkOrderStorno(\Order $order, array $data): bool { if ($data['OrderState'] === 'STORNO') { $order->storno(false); return true; } // storno rezervacnich objednavek if (findModule(\Modules::RESERVATIONS) && $order->source === OrderInfo::ORDER_SOURCE_RESERVATION) { $this->removeFlags($order, ['SNS', 'SNP', 'SNK', 'SZJ']); switch ($data['OrderState']) { case 'storno_nemame_skladem': $order->storno(false); $this->orderUtil->addFlag($order, 'SNS'); return true; case 'storno_neprisel': $order->storno(false); $this->orderUtil->addFlag($order, 'SNP'); return true; case 'storno_nic_nekoupil': $order->storno(false); $this->orderUtil->addFlag($order, 'SNK'); return true; case 'storno_zakoupil_jine_zbozi': $order->storno(false); $this->orderUtil->addFlag($order, 'SZJ'); return true; } } return false; } private function getOrderNewStatus(\Order $order, array $data): array { $newStatus = null; $orderMessage = null; $isStorno = false; if (findModule(\Modules::RESERVATIONS) && $order->source === OrderInfo::ORDER_SOURCE_RESERVATION) { return $this->getReservationOrderNewStatus($order, $data); } switch ($data['OrderState'] ?? false) { case 'INCOMPLETE': // nekompletni $newStatus = 1; break; case 'PICKHU': // priprava k expedici $newStatus = 5; break; case 'PICKING': // zahájeno vychystávání $newStatus = 2; break; case 'ORDERED': // poptano na prodejne #19014 - nastavit stav "Nová" $newStatus = 0; break; case 'PREPARED': case 'READYTOPIC': // připraveno k převzetí $newStatus = 4; // delaji to autopilotem, viz #16100 // $orderMessage = 'osobni_prevzeti'; break; case 'EXPED': case 'SENT': case 'INTRANSIT': // expedovano $newStatus = 6; // predano prepravci $orderMessage = 'predano_prepravci'; break; case 'RETHU': // expedovano $newStatus = 6; break; case 'FINISHED': case 'DELIVERED': case 'INVOICED': // vyřízeno $newStatus = 7; break; case 'CANCEL': // expedovano $newStatus = 6; $isStorno = true; break; } // u dropshipment objednavek nechceme posilat maily if ($this->isDropshipmentOrder($order)) { $orderMessage = null; } return [$newStatus, $orderMessage, $isStorno]; } private function getReservationOrderNewStatus(\Order $order, array $data): array { $newStatus = null; $orderMessage = null; switch ($data['OrderState'] ?? false) { case 'reminder': $orderMessage = 'rezervace_upominka'; break; case 'rezervovano': $newStatus = 4; $orderMessage = 'rezervace_pripravena'; break; case 'zakoupeno': case 'zakoupeno_plus_jine_zbozi': $this->removeFlags($order, ['ZAK', 'ZJZ']); $this->orderUtil->addFlag($order, $data['OrderState'] === 'zakoupeno' ? 'ZAK' : 'ZJZ'); $newStatus = 7; break; } return [$newStatus, $orderMessage, false]; } private function updateOrderTracking(\Order $order, array $data): void { // pokud mam modul balikonos, tak se o update trackingu stara `External\HannahBundle\SAP\Util\SAPOrderUpdater::updateOrderTracking` if (findModule(\Modules::BALIKONOS)) { return; } if (!($packageNumber = $this->getPackageNumber($data['Additionals'] ?? []))) { return; } sqlQueryBuilder() ->update('orders') ->directValues( [ 'package_id' => $packageNumber, ] ) ->where(Operator::equals(['id' => $order->id])) ->execute(); $order->package_id = $packageNumber; $order->logHistory( sprintf('[SAP] Trackovací čislo balíku: %s', $packageNumber) ); } public function updateStatusCustomDate(\Order $order, int $status): void { if (!($this->getStatusesDateSaveDefinition()[$status] ?? false)) { return; } $field = 'sapDate'.ucfirst($this->getStatusesDateSaveDefinition()[$status]); $order->setData($field, time()); } private function getStatusesDateSaveDefinition(): array { return [ 2 => 'picking', 4 => 'prepared', 6 => 'sent', ]; } private function removeFlags(\Order $order, array $flags): void { foreach ($flags as $flag) { if ($order->getFlags()[$flag] ?? false) { $this->orderUtil->removeFlag($order, $flag); } } } private function isDropshipmentOrder(\Order $order): bool { return $order->source === OrderInfo::ORDER_SOURCE_DROPSHIP; } protected function isLoggingEnabled(): bool { // always log orders return true; } }