我在使用控制台命令匯入訂單時嘗試使用自訂送貨方式。 一切正常,除了後端沒有顯示運輸方式,當我事後檢查訂單時: 截圖
模組基本結構和運輸方法是使用 mage2gen.com 產生的,如果我手動建立訂單,則可以在前端和後端運行。
有趣的是,如果我在收集運費後使用$shippingAddress->setShippingMethod('flatrate_flatrate');
,運輸方式設定正確,但我無法讓它與任何其他運輸方式或我的自訂運輸方式一起使用。
幫助將不勝感激!請告訴我是否應該添加更多程式碼。
謝謝! (對 Mage2 來說相當陌生)
系統.xml:
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="carriers" sortOrder="1000" showInWebsite="1" showInStore="1" showInDefault="1" translate="label"> <group id="hellocash" sortOrder="10" showInWebsite="1" showInStore="1" showInDefault="1" translate="label"> <label>HelloCash</label> <field id="active" type="select" sortOrder="10" showInWebsite="1" showInStore="1" showInDefault="1" translate="label"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="name" type="text" sortOrder="20" showInWebsite="1" showInStore="1" showInDefault="1" translate="label"> <label>Method Name</label> </field> <field id="price" type="text" sortOrder="30" showInWebsite="1" showInStore="1" showInDefault="1" translate="label"> <label>Price</label> <validate>validate-number validate-zero-or-greater</validate> </field> <field id="sort_order" type="text" sortOrder="40" showInWebsite="1" showInStore="1" showInDefault="1" translate="label"> <label>Sort Order</label> </field> <field id="title" type="text" sortOrder="50" showInWebsite="1" showInStore="1" showInDefault="1" translate="label"> <label>Title</label> </field> <field id="sallowspecific" type="select" sortOrder="60" showInWebsite="1" showInStore="1" showInDefault="1" translate="label"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> <field id="specificcountry" type="multiselect" sortOrder="70" showInWebsite="1" showInStore="1" showInDefault="1" translate="label"> <label>Ship to Specific Countries</label> <can_be_empty>1</can_be_empty> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> </field> <field id="specificerrmsg" type="textarea" sortOrder="80" showInWebsite="1" showInStore="1" showInDefault="1" translate="label"> <label>Displayed Error Message</label> </field> </group> </section> </system> </config>
運營商型號:
<?php /** * Copyright © Dan All rights reserved. * See COPYING.txt for license details. */ declare(strict_types=1); namespace Dan\HelloCash\Model\Carrier; use Magento\Quote\Model\Quote\Address\RateRequest; use Magento\Shipping\Model\Rate\Result; class HelloCash extends \Magento\Shipping\Model\Carrier\AbstractCarrier implements \Magento\Shipping\Model\Carrier\CarrierInterface { protected $_code = 'hellocash'; protected $_isFixed = true; protected $_rateResultFactory; protected $_rateMethodFactory; /** * Constructor * * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory * @param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory * @param array $data */ public function __construct( \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory, \Psr\Log\LoggerInterface $logger, \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory, \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory, array $data = [] ) { $this->_rateResultFactory = $rateResultFactory; $this->_rateMethodFactory = $rateMethodFactory; parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data); } /** * {@inheritdoc} */ public function collectRates(RateRequest $request) { if (!$this->getConfigFlag('active')) { return false; } $shippingPrice = $this->getConfigData('price'); $result = $this->_rateResultFactory->create(); if ($shippingPrice !== false) { $method = $this->_rateMethodFactory->create(); $method->setCarrier($this->_code); $method->setCarrierTitle($this->getConfigData('title')); $method->setMethod($this->_code); $method->setMethodTitle($this->getConfigData('name')); if ($request->getFreeShipping() === true) { $shippingPrice = '0.00'; } $method->setPrice($shippingPrice); $method->setCost($shippingPrice); $result->append($method); } return $result; } /** * getAllowedMethods * * @return array */ public function getAllowedMethods() { return [$this->_code => $this->getConfigData('name')]; } }
匯入訂單控制台命令:
<?php /** * Copyright © DAN All rights reserved. * See COPYING.txt for license details. */ declare(strict_types=1); namespace Dan\HelloCash\Console\Command; use Dan\HelloCash\Api\Data\SyncLogInterfaceFactory; use Dan\HelloCash\Api\HelloCash as HelloCashApi; use Dan\HelloCash\Enum\SyncLogType; use Dan\HelloCash\Exception\SyncException; use Dan\HelloCash\Exception\SyncOrderException; use Dan\HelloCash\Model\SyncLogRepository; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\ProductFactory; use Magento\Config\Model\Config; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Framework\App\State; use Magento\Framework\DB\Transaction; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\View\Result\PageFactory; use Magento\Quote\Model\Quote\Address\Rate as ShippingRate; use Magento\Quote\Model\QuoteManagement; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order\Email\Sender\OrderSender; use Magento\Store\Api\Data\StoreInterface; use Magento\Customer\Model\Customer\Interceptor as CustomerInterceptor; use Magento\Customer\Model\CustomerFactory; use Magento\Framework\App\Area; use Magento\Framework\Encryption\Encryptor; use Magento\Quote\Model\QuoteFactory; use Magento\Store\Model\StoreManagerInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Magento\Sales\Model\Service\InvoiceService; use Magento\Sales\Model\Order\Email\Sender\InvoiceSender; class SyncOrders extends SyncCommand { const NAME_ARGUMENT = 'name'; const NAME_OPTION = 'option'; protected CustomerFactory $customerFactory; protected CustomerInterceptor $customer; protected OutputInterface $output; protected StoreInterface $store; protected StoreManagerInterface $storeManager; protected QuoteFactory $quoteFactory; /** * @var PageFactory */ protected PageFactory $resultPageFactory; /** * @var OrderRepositoryInterface */ protected OrderRepositoryInterface $orderRepository; /** * @var ProductRepositoryInterface */ protected ProductRepositoryInterface $productRepository; /** * @var ProductRepositoryInterface */ protected $customerRepository; /** * @var QuoteFactory */ protected QuoteFactory $quote; /** * @var QuoteManagement */ protected QuoteManagement $quoteManagement; /** * @var OrderSender */ protected OrderSender $orderSender; protected ProductFactory $productFactory; protected ObjectManagerInterface $objectManager; protected ShippingRate $shippingRate; protected InvoiceService $invoiceService; protected Transaction $transaction; protected InvoiceSender $invoiceSender; private string $customerEmail; private string $customerPassword; private string $customerPhone; protected function __construct( HelloCashApi $api, Config $config, Encryptor $encryptor, SyncLogRepository $syncLogRepository, SyncLogInterfaceFactory $syncLogFactory, CustomerFactory $customerFactory, StoreManagerInterface $storeManager, CustomerRepositoryInterface $customerRepository, QuoteFactory $quote, ProductFactory $productFactory, ObjectManagerInterface $objectManager, ShippingRate $shippingRate, State $state, Transaction $transaction ) { $this->customerFactory = $customerFactory; $this->storeManager = $storeManager; $this->customerRepository = $customerRepository; $this->quote = $quote; $this->productFactory = $productFactory; $this->state = $state; $this->objectManager = $objectManager; $this->shippingRate = $shippingRate; $this->transaction = $transaction; parent::__construct($api, $config, $encryptor, $syncLogRepository, $syncLogFactory); $this->customerEmail = (string)$this->config->getConfigDataValue('hello_cash_general/sync_order/customer_email'); $this->customerPassword = $this->encryptor->decrypt($this->config->getConfigDataValue('hello_cash_general/sync_order/customer_password')); $this->customerPhone = (string)$this->config->getConfigDataValue('hello_cash_general/sync_order/customer_phone'); } /** * {@inheritdoc} */ protected function configure() { $this->setName('dan_hellocash:sync-orders'); $this->setDescription('Imports the HelloCash orders into your Magento shop.'); $this->setDefinition([ new InputArgument(self::NAME_ARGUMENT, InputArgument::OPTIONAL, 'Name'), new InputOption(self::NAME_OPTION, '-a', InputOption::VALUE_NONE, 'Option functionality') ]); parent::configure(); } /** * {@inheritdoc} */ protected function execute( InputInterface $input, OutputInterface $output ) { try { $this->output = $output; $this->state->setAreaCode(Area::AREA_FRONTEND); $this->store = $this->storeManager->getStore(); $this->websiteId = $this->storeManager->getWebsite()->getId(); $this->createSyncLog(SyncLogType::ORDERS); $ordersHellocash = $this->getOrders(); $this->syncLog->setTotal((int)$ordersHellocash->count); $this->syncLogRepository->save($this->syncLog); foreach ($ordersHellocash->invoices as $orderHellocash) { try { if ('1' === $orderHellocash->invoice_cancellation) { continue; } $orderHellocash = $this->getOrder((int)$orderHellocash->invoice_id); $this->createOrder($orderHellocash); } catch (SyncOrderException $exception) { $this->handleOrderError($orderHellocash, $exception); } finally { $this->syncLog->setProcessed($this->syncLog->getProcessed() + 1); $this->syncLogRepository->save($this->syncLog); } } $this->handleSuccess(); } catch (SyncException $exception) { $this->handleFatalError('A problem occurred calling the HelloCash API. (status: ' . $exception->getStatusCode() . ', message: "' . $exception->getMessage() . '")'); } finally { $this->finishSync(); } } protected function createOrder(\stdClass $orderHelloCash) { $this->createCustomer($orderHelloCash); $quote = $this->quote->create(); $quote->setStore($this->store); $quote->setCurrency(); /** * Registered Customer */ $customer = $this->customerRepository->getById($this->customer->getId()); $quote->assignCustomer($customer); foreach ($orderHelloCash->items as $item) { $product = $this->productFactory ->create() ->loadByAttribute('hellocash_article_id', (int)$item->item_article_id); if (!$product) { throw new SyncOrderException('Product not found. (HelloCash ID: ' . $item->item_article_id . ', name: "' . $item->item_name . '")'); } $product->setPrice((float)$item->item_price); $quote->addProduct( $product, (float)$item->item_quantity ); } $addressData = $this->createAddressData($orderHelloCash->company); $quote->getBillingAddress()->addData($addressData); $quote->getShippingAddress()->addData($addressData); $this->shippingRate ->setCode('hellocash_hellocash') ->getPrice(); $shippingAddress = $quote ->getShippingAddress() ->addShippingRate($this->shippingRate); $shippingAddress->setCollectShippingRates(true); $shippingAddress->collectShippingRates(); $shippingAddress->setShippingMethod('hellocash_hellocash'); $quote->setPaymentMethod('hellocash'); $quote->setInventoryProcessed(true); $quote->save(); $quote->getPayment()->importData(['method' => 'hellocash']); $quote->collectTotals()->save(); $quoteManagement = $this->objectManager->create(QuoteManagement::class); $order = $quoteManagement->submit($quote); $order ->setHellocashInvoiceId((int)$orderHelloCash->invoice_id) ->setEmailSent(1) ->setStatus('pending') ->save(); if (!$order->getEntityId()) { throw new SyncOrderException('Failed to get the order ID after saving.'); } $this->createInvoice($order); $order ->setStatus('complete') ->save(); return $order; } protected function createCustomer(\stdClass $invoiceHellocash): CustomerInterceptor { if (isset($this->customer)) { return $this->customer; } $this->verifyCustomerCredentials(); $this->customer = $this->customerFactory ->create() ->setWebsiteId($this->websiteId) ->setStore($this->store); if (!$this->customer->loadByEmail($this->customerEmail)->getId()) { $this->customer ->setFirstname('Cashier') ->setLastname('HelloCash') ->setEmail($this->customerEmail) ->setPassword($this->customerPassword); $this->customer->save(); /** * @TODO: Ask Daniel why this is not working. */ // $this->customerRepository->save($this->customer); } return $this->customer; } protected function createAddressData(\stdClass $companyHellocash): array { $address = [ 'firstname' => $companyHellocash->name, 'lastname' => 'HelloCash', 'country_id' => 'AT', 'city' => $companyHellocash->city, 'street' => $companyHellocash->street, //'region' => $companyHellocash->city, 'postcode' => $companyHellocash->postalCode, 'telephone' => $this->customerPhone, // 'fax' => $companyHellocash->postalCode, 'save_in_address_book' => 1 ]; return $address; } public function createInvoice($order) { $this->invoiceService = $this->objectManager->create(InvoiceService::class); $invoice = $this->invoiceService ->prepareInvoice($order) ->register() ->save(); $this->transaction ->addObject($invoice) ->addObject($invoice->getOrder()) ->save(); $this->invoiceSender = $this->objectManager->create(InvoiceSender::class); $this->invoiceSender->send($invoice); $order ->addCommentToStatusHistory(__('Notified customer about invoice creation #%1.', $invoice->getId())) ->setIsCustomerNotified(true) ->save(); return $invoice; } protected function verifyCustomerCredentials() { if (empty($this->customerEmail)) { throw new SyncException('The customer email is not set in the module\'s admin configuration! Please set it.'); } if (empty($this->customerPassword)) { throw new SyncException('The customer password is not set in the module\'s admin configuration! Please set it.'); } if (empty($this->customerPhone)) { throw new SyncException('The customer phone number is not set in the module\'s admin configuration! Please set it.'); } } }
問題在於一個衝突的、寫得不好的運輸插件。所以沒關係,感謝所有花時間閱讀我的程式碼的人。