diff --git a/Block/Adminhtml/Config/Form/Field/OrderStatuses.php b/Block/Adminhtml/Config/Form/Field/OrderStatuses.php new file mode 100644 index 0000000..12842e5 --- /dev/null +++ b/Block/Adminhtml/Config/Form/Field/OrderStatuses.php @@ -0,0 +1,92 @@ +addColumn('allegro_code', [ + 'label' => __('Allegro order status'), + 'class' => 'required-entry', + 'renderer' => $this->getAllegroStatusesRenderer() + ]); + $this->addColumn('magento_code', [ + 'label' => __('Magento order status'), + 'class' => 'required-entry', + 'renderer' => $this->getMagnetoStatusesRenderer() + ]); + + $this->_addAfter = false; + $this->_addButtonLabel = __('Add status'); + } + + /** + * @return MagentoOrderStatuses + * @throws LocalizedException + */ + protected function getMagnetoStatusesRenderer() + { + if (!$this->magentoBlockOptions) { + $this->magentoBlockOptions = $this->getLayout()->createBlock( + MagentoOrderStatuses::class, + '', + ['data' => ['is_render_to_js_template' => true]] + ); + } + + return $this->magentoBlockOptions; + } + + /** + * @return AllegroOrderStatuses + * @throws LocalizedException + */ + protected function getAllegroStatusesRenderer() + { + if (!$this->allegroBlockOptions) { + $this->allegroBlockOptions = $this->getLayout()->createBlock( + AllegroOrderStatuses::class, + '', + ['data' => ['is_render_to_js_template' => true]] + ); + } + + return $this->allegroBlockOptions; + } + + /** + * Prepare existing row data object. + * + * @param DataObject $row + * @throws LocalizedException + */ + protected function _prepareArrayRow(DataObject $row) + { + $options = []; + $customAttribute = $row->getData('magento_code'); + $key = 'option_' . $this->getMagnetoStatusesRenderer()->calcOptionHash($customAttribute); + $options[$key] = 'selected="selected"'; + $row->setData('option_extra_attrs', $options); + + $customAttribute = $row->getData('allegro_code'); + $key = 'option_' . $this->getAllegroStatusesRenderer()->calcOptionHash($customAttribute); + $options[$key] = 'selected="selected"'; + $row->setData('option_extra_attrs', $options); + } +} diff --git a/Block/Adminhtml/Config/Form/Field/Renderer/AllegroOrderStatuses.php b/Block/Adminhtml/Config/Form/Field/Renderer/AllegroOrderStatuses.php new file mode 100644 index 0000000..de5e0ae --- /dev/null +++ b/Block/Adminhtml/Config/Form/Field/Renderer/AllegroOrderStatuses.php @@ -0,0 +1,43 @@ +setName($value); + } + + /** + * @return array + */ + public function getAllegroOrderStatuses() + { + return [ + 'NEW' => __('NEW'), + 'PROCESSING' => __('PROCESSING'), + 'READY_FOR_SHIPMENT' => __('READY FOR SHIPMENT'), + 'READY_FOR_PICKUP' => __('READY FOR PICKUP'), + 'SENT' => __('SENT'), + 'PICKED_UP' => __('PICKED UP'), + 'CANCELLED' => __('CANCELLED') + ]; + } + /** + * @return Select + */ + protected function _prepareLayout() + { + $this->setOptions( + $this->getAllegroOrderStatuses() + ); + return parent::_prepareLayout(); + } +} diff --git a/Block/Adminhtml/Config/Form/Field/Renderer/MagentoOrderStatuses.php b/Block/Adminhtml/Config/Form/Field/Renderer/MagentoOrderStatuses.php new file mode 100644 index 0000000..1ca8512 --- /dev/null +++ b/Block/Adminhtml/Config/Form/Field/Renderer/MagentoOrderStatuses.php @@ -0,0 +1,51 @@ +orderStatusCollectionFactory = $orderStatusCollectionFactory; + } + + /** + * @param $value + * @return $this + */ + public function setInputName($value) + { + return $this->setName($value); + } + + /** + * @return $this + */ + protected function _prepareLayout() + { + $collection = $this->orderStatusCollectionFactory->create()->joinStates(); + foreach ($collection as $item) { + $this->addOption($item->getStatus(), __($item->getLabel())); + } + + return parent::_prepareLayout(); + } +} diff --git a/Model/AllegroOrderStatus.php b/Model/AllegroOrderStatus.php new file mode 100644 index 0000000..bf650bc --- /dev/null +++ b/Model/AllegroOrderStatus.php @@ -0,0 +1,70 @@ +scopeConfig = $scopeConfig; + $this->checkoutForm = $checkoutForm; + $this->logger = $logger; + } + + /** + * @param Order $order + * @return void + */ + public function updateOrderStatus(Order $order) + { + $statusesMapping = $this->scopeConfig->getValue(self::STATUSES_MAPPING_CONFIG_KEY); + $statusesMapping = json_decode($statusesMapping, true); + $statusesMapping = array_column($statusesMapping, 'allegro_code', 'magento_code'); + + $magentoStatus = $order->getStatus(); + $checkoutFormId = $order->getExternalId() ?: $order->getExtensionAttributes()->getExternalId(); + if (!isset($statusesMapping[$magentoStatus]) || !$checkoutFormId) { + return; + } + + try { + $this->checkoutForm->changeOrderStatus($checkoutFormId, $statusesMapping[$magentoStatus]); + $this->logger->info('Status on Allegro for order ' . $order->getRemoteIp() . ' has been updated'); + } catch (\Exception $e) { + $this->logger->exception( + $e, + 'Error while trying to update order ' . $order->getIncrementId() . ' status on Allegro: ' . $e->getMessage()//phpcs:ignore + ); + } + } +} diff --git a/Model/Api/Client.php b/Model/Api/Client.php index 9bef098..1fca922 100644 --- a/Model/Api/Client.php +++ b/Model/Api/Client.php @@ -178,6 +178,14 @@ private function sendHttpRequest(TokenInterface $token, Request $request) public function getResponse(GuzzleClient $client, string $method, string $uri, array $params) { $response = $client->request($method, $uri, $params); - return $response->getBody()->getContents(); + $contents = $response->getBody()->getContents(); + + if (empty($contents)) { + return $this->json->serialize( + ['statusCode' => $response->getStatusCode(), 'reasonPhrase' => $response->getReasonPhrase()] + ); + } + + return $contents; } } diff --git a/Model/Config/Backend/OrderStatuses.php b/Model/Config/Backend/OrderStatuses.php new file mode 100644 index 0000000..3c771ef --- /dev/null +++ b/Model/Config/Backend/OrderStatuses.php @@ -0,0 +1,74 @@ +serializer = $serializer; + parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data); + } + + /** + * @return void + */ + public function beforeSave() + { + /** @var array $value */ + $value = $this->getValue(); + unset($value['__empty']); + array_walk_recursive($value, function (&$v) { + $v = trim($v); + }); + $encodedValue = $this->serializer->serialize($value); + + $this->setValue($encodedValue); + } + + /** + * @return void + */ + protected function _afterLoad() + { + /** @var string $value */ + $value = $this->getValue(); + if ($value) { + $decodedValue = $this->serializer->unserialize($value); + } else { + $decodedValue = ''; + } + + $this->setValue($decodedValue); + } +} diff --git a/Model/ResourceModel/Order/CheckoutForm.php b/Model/ResourceModel/Order/CheckoutForm.php index 5bdb0e5..263eabb 100644 --- a/Model/ResourceModel/Order/CheckoutForm.php +++ b/Model/ResourceModel/Order/CheckoutForm.php @@ -3,6 +3,8 @@ namespace Macopedia\Allegro\Model\ResourceModel\Order; use Macopedia\Allegro\Model\Api\ClientException; +use Macopedia\Allegro\Model\Api\ClientResponseErrorException; +use Macopedia\Allegro\Model\Api\ClientResponseException; use Macopedia\Allegro\Model\ResourceModel\AbstractResource; /** @@ -14,8 +16,8 @@ class CheckoutForm extends AbstractResource * @param $checkoutFormId * @return array * @throws ClientException - * @throws \Macopedia\Allegro\Model\Api\ClientResponseErrorException - * @throws \Macopedia\Allegro\Model\Api\ClientResponseException + * @throws ClientResponseErrorException + * @throws ClientResponseException */ public function getCheckoutForm($checkoutFormId) { @@ -27,11 +29,24 @@ public function getCheckoutForm($checkoutFormId) * @param array $shippingData * @return array * @throws ClientException - * @throws \Macopedia\Allegro\Model\Api\ClientResponseErrorException - * @throws \Macopedia\Allegro\Model\Api\ClientResponseException + * @throws ClientResponseErrorException + * @throws ClientResponseException */ public function shipment($checkoutFormId, array $shippingData) { return $this->requestPost('order/checkout-forms/' . $checkoutFormId . '/shipments', $shippingData); } + + /** + * @param $checkoutFormId + * @param string $status + * @return array + * @throws ClientException + * @throws ClientResponseErrorException + * @throws ClientResponseException + */ + public function changeOrderStatus($checkoutFormId, string $status) + { + return $this->requestPut('order/checkout-forms/' . $checkoutFormId . '/fulfillment', ['status' => $status]); + } } diff --git a/Observer/SaveOrderAfterObserver.php b/Observer/SaveOrderAfterObserver.php new file mode 100644 index 0000000..63084ea --- /dev/null +++ b/Observer/SaveOrderAfterObserver.php @@ -0,0 +1,49 @@ +orderOrigin = $orderOrigin; + $this->allegroOrderStatus = $allegroOrderStatus; + } + + /** + * @param Observer $observer + * @return $this + */ + public function execute(Observer $observer) + { + $order = $observer->getEvent()->getOrder(); + + if (!$order->getId() || !$this->orderOrigin->isOrderFromAllegro($order)) { + return $this; + } + $this->allegroOrderStatus->updateOrderStatus($order); + + return $this; + } +} diff --git a/README.md b/README.md index 5fab758..2f53736 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,9 @@ Magento 2 - Allegro Integration Module source code is completely free and releas ## Features 1. Aktualizacja stanów magazynowych w Magento po sprzedaży produktu w Allegro oraz w Allegro po sprzedaży w Magento 2. Import zamówień z Allegro do Magento -3. Publikowanie ofert na Allegro z poziomu Magento -4. Wystawianie numerów przesyłek dla zamówień na Allegro z poziomu Magento +3. Automatyczna aktualizacja statusów zamówień z poziomu Magento +4. Publikowanie ofert na Allegro z poziomu Magento +5. Wystawianie numerów przesyłek dla zamówień na Allegro z poziomu Magento ## Kolejność działań po instalacji modułu 1. Dodać konfiguracje kolejki w [MySQL MQ](https://github.com/macopedia/magento2-allegro#konfiguracja-mysql-mq) lub [RabbitMQ](https://github.com/macopedia/magento2-allegro#konfiguracja-rabbitmq) @@ -111,6 +112,12 @@ Znajdują się tam informacje na temat powodu błędu, ilości prób zaimportowa ![grid](README/allegroOrdersWithErrorsGrid.png) +## Mapowanie statusów zamówień +W konfiguracji wtyczki możemy definiować mapowanie statusów zamówień pomiędzy Magento i Allegro. +![method_mapping](README/orderStatusesMapping.png) + +Po dodaniu odpowiedniego mapowania zmiana statusu zamówienia w Magento spowoduje również zmianę statusu na Allegro. + ## Mapowanie metod dostawy i płatności W konfiguracji wtyczki możemy definiować mapowanie metod płatności dla zamówień przychodzących z Allegro do sklepu Magento. ![method_mapping](README/deliveryAndPaymentMapping.png) @@ -119,6 +126,8 @@ Dla mapowania metod dostawy mamy do dyspozycji dynamiczną listę, do której mo Dla mapowania metod płatności mamy do dyspozycji dwie listy rozwijane, w których możemy wybrać po jednej z dostępnych i aktywnych w konfiguracji sklepu Magento metod płatności - dla zamówień przychodzących z Allegro z płatnością online i dla zamówień z płatnością przy pobraniu. +![grid](README/allegroOrdersWithErrorsGrid.png) + ## Wysyłanie numerów przesyłek Aby klient mógł śledzić przesyłkę z jego zamówieniem należy wprowadzić w Allegro jej numer oraz informacje o przewoźniku. Dzięki wtyczce można, to zrobić z poziomu Magento: 1. Należy wejść na stronę zamówienia, które zostało wcześniej zaimportowane z Allegro i otworzyć zakładkę 'Dostawa'. @@ -132,20 +141,6 @@ Aby klient mógł śledzić przesyłkę z jego zamówieniem należy wprowadzić Wysyłkę numerów przesyłek można włączać lub wyłączać w konfiguracji wtyczki. -## Automatyczna zmiana statusu zamówienia na "Wysłane" w panelu Allegro - -W panelu Allegro można ustawić automatyczną zmianę statusów na "Wysłane" po wprowadzeniu listu przewozowego do danego zamówienia. Należy jednorazowo wprowadzić ręcznie numer przesyłki dla danego zamówienia lub edytować już istniejący i zaznaczyć checkbox "Automatycznie zmieniaj status wszystkich zamówień na WYSŁANE po dodaniu numeru przesyłki". -Aby to zrobić należy: -1. W panelu Magento, wygenerować etykietę wysyłkową dla zamowienia zaimportowanego z Allegro -2. Skopiować wygenerowany numer przesyłki -3. Zalogować się do platformy Allegro i wybrać zamówienie, do którego chcemy wprowadzić numer przesyłki lub zmienić już istniejący -4. Na liście zamówień na Allegro, przy zamówieniu do którego chcemy wprowadzić numer przesyłki, klikamy w listę rozwijaną AKCJE → wybieramy opcję NUMERY PRZESYŁEK -5. Otworzy nam się osobne okno z opcja do wybrania przewoźnika oraz do wprowadzenia numeru przesyłki. -6. W otwartym oknie należy zaznaczyć checkbox "Automatycznie zmieniaj status wszystkich zamówień na WYSŁANE po dodaniu numeru przesyłki" i kliknąć "ZAPISZ" - -Po tym ustawieniu wszystkie zamówienia, które są na Allegro, które otrzymają z Magento numer listu przewozowego, mają status "Wysłane". - - ## Publikowanie ofert Za pomocą wtyczki możemy wystawiać produkty z Magento na Allegro. Aby to zrobić należy: 1. Na koncie Allegro utworzyć [cennik dostaw](https://allegro.pl/pomoc/dla-sprzedajacych/cennik-dostawy/cenniki-dostawy-tworzenie-edycja-i-podmiana-B826XYWjvFg) diff --git a/README/allegroOrderImporterConfiguration.png b/README/allegroOrderImporterConfiguration.png index fc6a91c..df138a1 100644 Binary files a/README/allegroOrderImporterConfiguration.png and b/README/allegroOrderImporterConfiguration.png differ diff --git a/README/orderStatusesMapping.png b/README/orderStatusesMapping.png new file mode 100644 index 0000000..2026003 Binary files /dev/null and b/README/orderStatusesMapping.png differ diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index f5456f0..1e058ba 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -97,6 +97,11 @@ Macopedia\Allegro\Model\Config\Source\State + + + \Macopedia\Allegro\Block\Adminhtml\Config\Form\Field\OrderStatuses + Macopedia\Allegro\Model\Config\Backend\OrderStatuses + diff --git a/etc/events.xml b/etc/events.xml index 2718f14..b4355d6 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -14,4 +14,7 @@ + + + diff --git a/i18n/pl_PL.csv b/i18n/pl_PL.csv index 90d8258..04849b5 100644 --- a/i18n/pl_PL.csv +++ b/i18n/pl_PL.csv @@ -183,3 +183,14 @@ "Allegro overpayment","Allegro nadpłata" "Cron removes Allegro offer ID from product when offer no longer exists","Cron usuwa z produktu ID oferty na Allegro jeśli oferta już nie istnieje" "Cron for cleaning old offers mapping enabled","Cron do czyszczenia starych mapowań ofert" +"NEW","NOWE" +"PROCESSING","W REALIZACJI" +"READY FOR SHIPMENT","DO WYSŁANIA" +"READY FOR PICKUP","DO ODBIORU" +"SENT","WYSŁANE" +"PICKED UP","ODEBRANE" +"CANCELLED","ANULOWANE" +"Allegro order status","Status zamówień Allegro" +"Magento order status","Status zamówień Magento" +"Order statuses mapping","Mapowanie statusów zamówień" +"Add status","Dodaj status"